| 1 | --- /dev/null |
| 2 | +++ b/Documentation/arm/OMAP/DSS |
| 3 | @@ -0,0 +1,317 @@ |
| 4 | +OMAP2/3 Display Subsystem |
| 5 | +------------------------- |
| 6 | + |
| 7 | +This is an almost total rewrite of the OMAP FB driver in drivers/video/omap |
| 8 | +(let's call it DSS1). The main differences between DSS1 and DSS2 are DSI, |
| 9 | +TV-out and multiple display support, but there are lots of small improvements |
| 10 | +also. |
| 11 | + |
| 12 | +The DSS2 driver (omapdss module) is in arch/arm/plat-omap/dss/, and the FB, |
| 13 | +panel and controller drivers are in drivers/video/omap2/. DSS1 and DSS2 live |
| 14 | +currently side by side, you can choose which one to use. |
| 15 | + |
| 16 | +Features |
| 17 | +-------- |
| 18 | + |
| 19 | +Working and tested features include: |
| 20 | + |
| 21 | +- MIPI DPI (parallel) output |
| 22 | +- MIPI DSI output in command mode |
| 23 | +- MIPI DBI (RFBI) output |
| 24 | +- SDI output |
| 25 | +- TV output |
| 26 | +- All pieces can be compiled as a module or inside kernel |
| 27 | +- Use DISPC to update any of the outputs |
| 28 | +- Use CPU to update RFBI or DSI output |
| 29 | +- OMAP DISPC planes |
| 30 | +- RGB16, RGB24 packed, RGB24 unpacked |
| 31 | +- YUV2, UYVY |
| 32 | +- Scaling |
| 33 | +- Adjusting DSS FCK to find a good pixel clock |
| 34 | +- Use DSI DPLL to create DSS FCK |
| 35 | + |
| 36 | +Tested boards include: |
| 37 | +- OMAP3 SDP board |
| 38 | +- Beagle board |
| 39 | +- N810 |
| 40 | + |
| 41 | +omapdss driver |
| 42 | +-------------- |
| 43 | + |
| 44 | +The DSS driver does not itself have any support for Linux framebuffer, V4L or |
| 45 | +such like the current ones, but it has an internal kernel API that upper level |
| 46 | +drivers can use. |
| 47 | + |
| 48 | +The DSS driver models OMAP's overlays, overlay managers and displays in a |
| 49 | +flexible way to enable non-common multi-display configuration. In addition to |
| 50 | +modelling the hardware overlays, omapdss supports virtual overlays and overlay |
| 51 | +managers. These can be used when updating a display with CPU or system DMA. |
| 52 | + |
| 53 | +Panel and controller drivers |
| 54 | +---------------------------- |
| 55 | + |
| 56 | +The drivers implement panel or controller specific functionality and are not |
| 57 | +usually visible to users except through omapfb driver. They register |
| 58 | +themselves to the DSS driver. |
| 59 | + |
| 60 | +omapfb driver |
| 61 | +------------- |
| 62 | + |
| 63 | +The omapfb driver implements arbitrary number of standard linux framebuffers. |
| 64 | +These framebuffers can be routed flexibly to any overlays, thus allowing very |
| 65 | +dynamic display architecture. |
| 66 | + |
| 67 | +The driver exports some omapfb specific ioctls, which are compatible with the |
| 68 | +ioctls in the old driver. |
| 69 | + |
| 70 | +The rest of the non standard features are exported via sysfs. Whether the final |
| 71 | +implementation will use sysfs, or ioctls, is still open. |
| 72 | + |
| 73 | +V4L2 drivers |
| 74 | +------------ |
| 75 | + |
| 76 | +V4L2 is being implemented in TI. |
| 77 | + |
| 78 | +From omapdss point of view the V4L2 drivers should be similar to framebuffer |
| 79 | +driver. |
| 80 | + |
| 81 | +Architecture |
| 82 | +-------------------- |
| 83 | + |
| 84 | +Some clarification what the different components do: |
| 85 | + |
| 86 | + - Framebuffer is a memory area inside OMAP's SRAM/SDRAM that contains the |
| 87 | + pixel data for the image. Framebuffer has width and height and color |
| 88 | + depth. |
| 89 | + - Overlay defines where the pixels are read from and where they go on the |
| 90 | + screen. The overlay may be smaller than framebuffer, thus displaying only |
| 91 | + part of the framebuffer. The position of the overlay may be changed if |
| 92 | + the overlay is smaller than the display. |
| 93 | + - Overlay manager combines the overlays in to one image and feeds them to |
| 94 | + display. |
| 95 | + - Display is the actual physical display device. |
| 96 | + |
| 97 | +A framebuffer can be connected to multiple overlays to show the same pixel data |
| 98 | +on all of the overlays. Note that in this case the overlay input sizes must be |
| 99 | +the same, but, in case of video overlays, the output size can be different. Any |
| 100 | +framebuffer can be connected to any overlay. |
| 101 | + |
| 102 | +An overlay can be connected to one overlay manager. Also DISPC overlays can be |
| 103 | +connected only to DISPC overlay managers, and virtual overlays can be only |
| 104 | +connected to virtual overlays. |
| 105 | + |
| 106 | +An overlay manager can be connected to one display. There are certain |
| 107 | +restrictions which kinds of displays an overlay manager can be connected: |
| 108 | + |
| 109 | + - DISPC TV overlay manager can be only connected to TV display. |
| 110 | + - Virtual overlay managers can only be connected to DBI or DSI displays. |
| 111 | + - DISPC LCD overlay manager can be connected to all displays, except TV |
| 112 | + display. |
| 113 | + |
| 114 | +Sysfs |
| 115 | +----- |
| 116 | +The sysfs interface is mainly used for testing. I don't think sysfs |
| 117 | +interface is the best for this in the final version, but I don't quite know |
| 118 | +what would be the best interfaces for these things. |
| 119 | + |
| 120 | +The sysfs interface is divided to two parts: DSS and FB. |
| 121 | + |
| 122 | +/sys/class/graphics/fb? directory: |
| 123 | +mirror 0=off, 1=on |
| 124 | +rotate Rotation 0-3 for 0, 90, 180, 270 degrees |
| 125 | +rotate_type 0 = DMA rotation, 1 = VRFB rotation |
| 126 | +overlays List of overlay numbers to which framebuffer pixels go |
| 127 | +phys_addr Physical address of the framebuffer |
| 128 | +virt_addr Virtual address of the framebuffer |
| 129 | +size Size of the framebuffer |
| 130 | + |
| 131 | +/sys/devices/platform/omapdss/overlay? directory: |
| 132 | +enabled 0=off, 1=on |
| 133 | +input_size width,height (ie. the framebuffer size) |
| 134 | +manager Destination overlay manager name |
| 135 | +name |
| 136 | +output_size width,height |
| 137 | +position x,y |
| 138 | +screen_width width |
| 139 | +global_alpha global alpha 0-255 0=transparent 255=opaque |
| 140 | + |
| 141 | +/sys/devices/platform/omapdss/manager? directory: |
| 142 | +display Destination display |
| 143 | +name |
| 144 | +alpha_blending_enabled 0=off, 1=on |
| 145 | +trans_key_enabled 0=off, 1=on |
| 146 | +trans_key_type gfx-destination, video-source |
| 147 | +trans_key_value transparency color key (RGB24) |
| 148 | +default_color default background color (RGB24) |
| 149 | + |
| 150 | +/sys/devices/platform/omapdss/display? directory: |
| 151 | +ctrl_name Controller name |
| 152 | +mirror 0=off, 1=on |
| 153 | +update_mode 0=off, 1=auto, 2=manual |
| 154 | +enabled 0=off, 1=on |
| 155 | +name |
| 156 | +rotate Rotation 0-3 for 0, 90, 180, 270 degrees |
| 157 | +timings Display timings (pixclock,xres/hfp/hbp/hsw,yres/vfp/vbp/vsw) |
| 158 | + When writing, two special timings are accepted for tv-out: |
| 159 | + "pal" and "ntsc" |
| 160 | +panel_name |
| 161 | +tear_elim Tearing elimination 0=off, 1=on |
| 162 | + |
| 163 | +There are also some debugfs files at <debugfs>/omapdss/ which show information |
| 164 | +about clocks and registers. |
| 165 | + |
| 166 | +Examples |
| 167 | +-------- |
| 168 | + |
| 169 | +The following definitions have been made for the examples below: |
| 170 | + |
| 171 | +ovl0=/sys/devices/platform/omapdss/overlay0 |
| 172 | +ovl1=/sys/devices/platform/omapdss/overlay1 |
| 173 | +ovl2=/sys/devices/platform/omapdss/overlay2 |
| 174 | + |
| 175 | +mgr0=/sys/devices/platform/omapdss/manager0 |
| 176 | +mgr1=/sys/devices/platform/omapdss/manager1 |
| 177 | + |
| 178 | +lcd=/sys/devices/platform/omapdss/display0 |
| 179 | +dvi=/sys/devices/platform/omapdss/display1 |
| 180 | +tv=/sys/devices/platform/omapdss/display2 |
| 181 | + |
| 182 | +fb0=/sys/class/graphics/fb0 |
| 183 | +fb1=/sys/class/graphics/fb1 |
| 184 | +fb2=/sys/class/graphics/fb2 |
| 185 | + |
| 186 | +Default setup on OMAP3 SDP |
| 187 | +-------------------------- |
| 188 | + |
| 189 | +Here's the default setup on OMAP3 SDP board. All planes go to LCD. DVI |
| 190 | +and TV-out are not in use. The columns from left to right are: |
| 191 | +framebuffers, overlays, overlay managers, displays. Framebuffers are |
| 192 | +handled by omapfb, and the rest by the DSS. |
| 193 | + |
| 194 | +FB0 --- GFX -\ DVI |
| 195 | +FB1 --- VID1 --+- LCD ---- LCD |
| 196 | +FB2 --- VID2 -/ TV ----- TV |
| 197 | + |
| 198 | +Example: Switch from LCD to DVI |
| 199 | +---------------------- |
| 200 | + |
| 201 | +w=`cat $dvi/timings | cut -d "," -f 2 | cut -d "/" -f 1` |
| 202 | +h=`cat $dvi/timings | cut -d "," -f 3 | cut -d "/" -f 1` |
| 203 | + |
| 204 | +echo "0" > $lcd/enabled |
| 205 | +echo "" > $mgr0/display |
| 206 | +fbset -fb /dev/fb0 -xres $w -yres $h -vxres $w -vyres $h |
| 207 | +# at this point you have to switch the dvi/lcd dip-switch from the omap board |
| 208 | +echo "dvi" > $mgr0/display |
| 209 | +echo "1" > $dvi/enabled |
| 210 | + |
| 211 | +After this the configuration looks like: |
| 212 | + |
| 213 | +FB0 --- GFX -\ -- DVI |
| 214 | +FB1 --- VID1 --+- LCD -/ LCD |
| 215 | +FB2 --- VID2 -/ TV ----- TV |
| 216 | + |
| 217 | +Example: Clone GFX overlay to LCD and TV |
| 218 | +------------------------------- |
| 219 | + |
| 220 | +w=`cat $tv/timings | cut -d "," -f 2 | cut -d "/" -f 1` |
| 221 | +h=`cat $tv/timings | cut -d "," -f 3 | cut -d "/" -f 1` |
| 222 | + |
| 223 | +echo "0" > $ovl0/enabled |
| 224 | +echo "0" > $ovl1/enabled |
| 225 | + |
| 226 | +echo "" > $fb1/overlays |
| 227 | +echo "0,1" > $fb0/overlays |
| 228 | + |
| 229 | +echo "$w,$h" > $ovl1/output_size |
| 230 | +echo "tv" > $ovl1/manager |
| 231 | + |
| 232 | +echo "1" > $ovl0/enabled |
| 233 | +echo "1" > $ovl1/enabled |
| 234 | + |
| 235 | +echo "1" > $tv/enabled |
| 236 | + |
| 237 | +After this the configuration looks like (only relevant parts shown): |
| 238 | + |
| 239 | +FB0 +-- GFX ---- LCD ---- LCD |
| 240 | + \- VID1 ---- TV ---- TV |
| 241 | + |
| 242 | +Misc notes |
| 243 | +---------- |
| 244 | + |
| 245 | +OMAP FB allocates the framebuffer memory using the OMAP VRAM allocator. |
| 246 | + |
| 247 | +Using DSI DPLL to generate pixel clock it is possible produce the pixel clock |
| 248 | +of 86.5MHz (max possible), and with that you get 1280x1024@57 output from DVI. |
| 249 | + |
| 250 | +Rotation and mirroring currently only supports RGB565 and RGB8888 modes. VRFB |
| 251 | +does not support mirroring. |
| 252 | + |
| 253 | +VRFB rotation requires much more memory than non-rotated framebuffer, so you |
| 254 | +probably need to increase your vram setting before using VRFB rotation. Also, |
| 255 | +many applications may not work with VRFB if they do not pay attention to all |
| 256 | +framebuffer parameters. |
| 257 | + |
| 258 | +Kernel boot arguments |
| 259 | +--------------------- |
| 260 | + |
| 261 | +vram=<size> |
| 262 | + - Amount of total VRAM to preallocate. For example, "10M". omapfb |
| 263 | + allocates memory for framebuffers from VRAM. |
| 264 | + |
| 265 | +omapfb.mode=<display>:<mode>[,...] |
| 266 | + - Default video mode for specified displays. For example, |
| 267 | + "dvi:800x400MR-24@60". See drivers/video/modedb.c. |
| 268 | + There are also two special modes: "pal" and "ntsc" that |
| 269 | + can be used to tv out. |
| 270 | + |
| 271 | +omapfb.vram=<fbnum>:<size>[@<physaddr>][,...] |
| 272 | + - VRAM allocated for a framebuffer. Normally omapfb allocates vram |
| 273 | + depending on the display size. With this you can manually allocate |
| 274 | + more or define the physical address of each framebuffer. For example, |
| 275 | + "1:4M" to allocate 4M for fb1. |
| 276 | + |
| 277 | +omapfb.debug=<y|n> |
| 278 | + - Enable debug printing. You have to have OMAPFB debug support enabled |
| 279 | + in kernel config. |
| 280 | + |
| 281 | +omapfb.test=<y|n> |
| 282 | + - Draw test pattern to framebuffer whenever framebuffer settings change. |
| 283 | + You need to have OMAPFB debug support enabled in kernel config. |
| 284 | + |
| 285 | +omapfb.vrfb=<y|n> |
| 286 | + - Use VRFB rotation for all framebuffers. |
| 287 | + |
| 288 | +omapfb.rotate=<angle> |
| 289 | + - Default rotation applied to all framebuffers. |
| 290 | + 0 - 0 degree rotation |
| 291 | + 1 - 90 degree rotation |
| 292 | + 2 - 180 degree rotation |
| 293 | + 3 - 270 degree rotation |
| 294 | + |
| 295 | +omapfb.mirror=<y|n> |
| 296 | + - Default mirror for all framebuffers. Only works with DMA rotation. |
| 297 | + |
| 298 | +omapdss.def_disp=<display> |
| 299 | + - Name of default display, to which all overlays will be connected. |
| 300 | + Common examples are "lcd" or "tv". |
| 301 | + |
| 302 | +omapdss.debug=<y|n> |
| 303 | + - Enable debug printing. You have to have DSS debug support enabled in |
| 304 | + kernel config. |
| 305 | + |
| 306 | +TODO |
| 307 | +---- |
| 308 | + |
| 309 | +DSS locking |
| 310 | + |
| 311 | +Error checking |
| 312 | +- Lots of checks are missing or implemented just as BUG() |
| 313 | + |
| 314 | +System DMA update for DSI |
| 315 | +- Can be used for RGB16 and RGB24P modes. Probably not for RGB24U (how |
| 316 | + to skip the empty byte?) |
| 317 | + |
| 318 | +OMAP1 support |
| 319 | +- Not sure if needed |
| 320 | + |
| 321 | --- a/MAINTAINERS |
| 322 | +++ b/MAINTAINERS |
| 323 | @@ -3848,6 +3848,23 @@ L: linux-omap@vger.kernel.org |
| 324 | S: Maintained |
| 325 | F: drivers/video/omap/ |
| 326 | |
| 327 | +OMAP DISPLAY SUBSYSTEM SUPPORT (DSS2) |
| 328 | +M: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 329 | +L: linux-fbdev-devel@lists.sourceforge.net (moderated for non-subscribers) |
| 330 | +L: linux-omap@vger.kernel.org |
| 331 | +S: Maintained |
| 332 | +F: drivers/video/omap2/dss/ |
| 333 | +F: drivers/video/omap2/vrfb.c |
| 334 | +F: drivers/video/omap2/vram.c |
| 335 | +F: Documentation/arm/OMAP/DSS |
| 336 | + |
| 337 | +OMAP FRAMEBUFFER SUPPORT (FOR DSS2) |
| 338 | +M: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 339 | +L: linux-fbdev-devel@lists.sourceforge.net (moderated for non-subscribers) |
| 340 | +L: linux-omap@vger.kernel.org |
| 341 | +S: Maintained |
| 342 | +F: drivers/video/omap2/omapfb/ |
| 343 | + |
| 344 | OMAP MMC SUPPORT |
| 345 | M: Jarkko Lavinen <jarkko.lavinen@nokia.com> |
| 346 | L: linux-omap@vger.kernel.org |
| 347 | --- a/arch/arm/configs/omap_3430sdp_defconfig |
| 348 | +++ b/arch/arm/configs/omap_3430sdp_defconfig |
| 349 | @@ -1336,10 +1336,33 @@ CONFIG_FB_CFB_IMAGEBLIT=y |
| 350 | # |
| 351 | # CONFIG_FB_S1D13XXX is not set |
| 352 | # CONFIG_FB_VIRTUAL is not set |
| 353 | -CONFIG_FB_OMAP=y |
| 354 | -# CONFIG_FB_OMAP_LCDC_EXTERNAL is not set |
| 355 | +# CONFIG_FB_METRONOME is not set |
| 356 | +# CONFIG_FB_MB862XX is not set |
| 357 | +# CONFIG_FB_BROADSHEET is not set |
| 358 | +# CONFIG_FB_OMAP_LCD_VGA is not set |
| 359 | # CONFIG_FB_OMAP_BOOTLOADER_INIT is not set |
| 360 | -CONFIG_FB_OMAP_CONSISTENT_DMA_SIZE=2 |
| 361 | +CONFIG_OMAP2_VRAM=y |
| 362 | +CONFIG_OMAP2_VRFB=y |
| 363 | +CONFIG_OMAP2_DSS=y |
| 364 | +CONFIG_OMAP2_VRAM_SIZE=4 |
| 365 | +CONFIG_OMAP2_DSS_DEBUG_SUPPORT=y |
| 366 | +# CONFIG_OMAP2_DSS_RFBI is not set |
| 367 | +CONFIG_OMAP2_DSS_VENC=y |
| 368 | +# CONFIG_OMAP2_DSS_SDI is not set |
| 369 | +# CONFIG_OMAP2_DSS_DSI is not set |
| 370 | +# CONFIG_OMAP2_DSS_FAKE_VSYNC is not set |
| 371 | +CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK=0 |
| 372 | +CONFIG_FB_OMAP2=y |
| 373 | +CONFIG_FB_OMAP2_DEBUG_SUPPORT=y |
| 374 | +# CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE is not set |
| 375 | +CONFIG_FB_OMAP2_NUM_FBS=3 |
| 376 | + |
| 377 | +# |
| 378 | +# OMAP2/3 Display Device Drivers |
| 379 | +# |
| 380 | +CONFIG_PANEL_GENERIC=y |
| 381 | +# CONFIG_PANEL_SAMSUNG_LTE430WQ_F0C is not set |
| 382 | +CONFIG_PANEL_SHARP_LS037V7DW01=y |
| 383 | # CONFIG_BACKLIGHT_LCD_SUPPORT is not set |
| 384 | |
| 385 | # |
| 386 | --- a/arch/arm/mach-omap1/board-nokia770.c |
| 387 | +++ b/arch/arm/mach-omap1/board-nokia770.c |
| 388 | @@ -14,6 +14,7 @@ |
| 389 | #include <linux/platform_device.h> |
| 390 | #include <linux/input.h> |
| 391 | #include <linux/clk.h> |
| 392 | +#include <linux/omapfb.h> |
| 393 | |
| 394 | #include <linux/spi/spi.h> |
| 395 | #include <linux/spi/ads7846.h> |
| 396 | @@ -32,7 +33,6 @@ |
| 397 | #include <mach/keypad.h> |
| 398 | #include <mach/common.h> |
| 399 | #include <mach/dsp_common.h> |
| 400 | -#include <mach/omapfb.h> |
| 401 | #include <mach/hwa742.h> |
| 402 | #include <mach/lcd_mipid.h> |
| 403 | #include <mach/mmc.h> |
| 404 | --- a/arch/arm/mach-omap2/board-3430sdp.c |
| 405 | +++ b/arch/arm/mach-omap2/board-3430sdp.c |
| 406 | @@ -37,6 +37,7 @@ |
| 407 | #include <mach/common.h> |
| 408 | #include <mach/dma.h> |
| 409 | #include <mach/gpmc.h> |
| 410 | +#include <mach/display.h> |
| 411 | |
| 412 | #include <mach/control.h> |
| 413 | #include <mach/gpmc-smc91x.h> |
| 414 | @@ -152,31 +153,152 @@ static struct spi_board_info sdp3430_spi |
| 415 | }, |
| 416 | }; |
| 417 | |
| 418 | -static struct platform_device sdp3430_lcd_device = { |
| 419 | - .name = "sdp2430_lcd", |
| 420 | - .id = -1, |
| 421 | + |
| 422 | +#define SDP3430_LCD_PANEL_BACKLIGHT_GPIO 8 |
| 423 | +#define SDP3430_LCD_PANEL_ENABLE_GPIO 5 |
| 424 | + |
| 425 | +static unsigned backlight_gpio; |
| 426 | +static unsigned enable_gpio; |
| 427 | +static int lcd_enabled; |
| 428 | +static int dvi_enabled; |
| 429 | + |
| 430 | +static void __init sdp3430_display_init(void) |
| 431 | +{ |
| 432 | + int r; |
| 433 | + |
| 434 | + enable_gpio = SDP3430_LCD_PANEL_ENABLE_GPIO; |
| 435 | + backlight_gpio = SDP3430_LCD_PANEL_BACKLIGHT_GPIO; |
| 436 | + |
| 437 | + r = gpio_request(enable_gpio, "LCD reset"); |
| 438 | + if (r) { |
| 439 | + printk(KERN_ERR "failed to get LCD reset GPIO\n"); |
| 440 | + goto err0; |
| 441 | + } |
| 442 | + |
| 443 | + r = gpio_request(backlight_gpio, "LCD Backlight"); |
| 444 | + if (r) { |
| 445 | + printk(KERN_ERR "failed to get LCD backlight GPIO\n"); |
| 446 | + goto err1; |
| 447 | + } |
| 448 | + |
| 449 | + gpio_direction_output(enable_gpio, 0); |
| 450 | + gpio_direction_output(backlight_gpio, 0); |
| 451 | + |
| 452 | + return; |
| 453 | +err1: |
| 454 | + gpio_free(enable_gpio); |
| 455 | +err0: |
| 456 | + return; |
| 457 | +} |
| 458 | + |
| 459 | +static int sdp3430_panel_enable_lcd(struct omap_dss_device *dssdev) |
| 460 | +{ |
| 461 | + if (dvi_enabled) { |
| 462 | + printk(KERN_ERR "cannot enable LCD, DVI is enabled\n"); |
| 463 | + return -EINVAL; |
| 464 | + } |
| 465 | + |
| 466 | + gpio_direction_output(enable_gpio, 1); |
| 467 | + gpio_direction_output(backlight_gpio, 1); |
| 468 | + |
| 469 | + lcd_enabled = 1; |
| 470 | + |
| 471 | + return 0; |
| 472 | +} |
| 473 | + |
| 474 | +static void sdp3430_panel_disable_lcd(struct omap_dss_device *dssdev) |
| 475 | +{ |
| 476 | + lcd_enabled = 0; |
| 477 | + |
| 478 | + gpio_direction_output(enable_gpio, 0); |
| 479 | + gpio_direction_output(backlight_gpio, 0); |
| 480 | +} |
| 481 | + |
| 482 | +static int sdp3430_panel_enable_dvi(struct omap_dss_device *dssdev) |
| 483 | +{ |
| 484 | + if (lcd_enabled) { |
| 485 | + printk(KERN_ERR "cannot enable DVI, LCD is enabled\n"); |
| 486 | + return -EINVAL; |
| 487 | + } |
| 488 | + |
| 489 | + dvi_enabled = 1; |
| 490 | + |
| 491 | + return 0; |
| 492 | +} |
| 493 | + |
| 494 | +static void sdp3430_panel_disable_dvi(struct omap_dss_device *dssdev) |
| 495 | +{ |
| 496 | + dvi_enabled = 0; |
| 497 | +} |
| 498 | + |
| 499 | +static int sdp3430_panel_enable_tv(struct omap_dss_device *dssdev) |
| 500 | +{ |
| 501 | + return 0; |
| 502 | +} |
| 503 | + |
| 504 | +static void sdp3430_panel_disable_tv(struct omap_dss_device *dssdev) |
| 505 | +{ |
| 506 | +} |
| 507 | + |
| 508 | + |
| 509 | +static struct omap_dss_device sdp3430_lcd_device = { |
| 510 | + .name = "lcd", |
| 511 | + .driver_name = "sharp_ls_panel", |
| 512 | + .type = OMAP_DISPLAY_TYPE_DPI, |
| 513 | + .phy.dpi.data_lines = 16, |
| 514 | + .platform_enable = sdp3430_panel_enable_lcd, |
| 515 | + .platform_disable = sdp3430_panel_disable_lcd, |
| 516 | +}; |
| 517 | + |
| 518 | +static struct omap_dss_device sdp3430_dvi_device = { |
| 519 | + .name = "dvi", |
| 520 | + .driver_name = "generic_panel", |
| 521 | + .type = OMAP_DISPLAY_TYPE_DPI, |
| 522 | + .phy.dpi.data_lines = 24, |
| 523 | + .platform_enable = sdp3430_panel_enable_dvi, |
| 524 | + .platform_disable = sdp3430_panel_disable_dvi, |
| 525 | +}; |
| 526 | + |
| 527 | +static struct omap_dss_device sdp3430_tv_device = { |
| 528 | + .name = "tv", |
| 529 | + .driver_name = "venc", |
| 530 | + .type = OMAP_DISPLAY_TYPE_VENC, |
| 531 | + .phy.venc.type = OMAP_DSS_VENC_TYPE_SVIDEO, |
| 532 | + .platform_enable = sdp3430_panel_enable_tv, |
| 533 | + .platform_disable = sdp3430_panel_disable_tv, |
| 534 | +}; |
| 535 | + |
| 536 | + |
| 537 | +static struct omap_dss_device *sdp3430_dss_devices[] = { |
| 538 | + &sdp3430_lcd_device, |
| 539 | + &sdp3430_dvi_device, |
| 540 | + &sdp3430_tv_device, |
| 541 | }; |
| 542 | |
| 543 | -static struct regulator_consumer_supply sdp3430_vdac_supply = { |
| 544 | - .supply = "vdac", |
| 545 | - .dev = &sdp3430_lcd_device.dev, |
| 546 | +static struct omap_dss_board_info sdp3430_dss_data = { |
| 547 | + .num_devices = ARRAY_SIZE(sdp3430_dss_devices), |
| 548 | + .devices = sdp3430_dss_devices, |
| 549 | + .default_device = &sdp3430_lcd_device, |
| 550 | }; |
| 551 | |
| 552 | -static struct regulator_consumer_supply sdp3430_vdvi_supply = { |
| 553 | - .supply = "vdvi", |
| 554 | - .dev = &sdp3430_lcd_device.dev, |
| 555 | +static struct platform_device sdp3430_dss_device = { |
| 556 | + .name = "omapdss", |
| 557 | + .id = -1, |
| 558 | + .dev = { |
| 559 | + .platform_data = &sdp3430_dss_data, |
| 560 | + }, |
| 561 | }; |
| 562 | |
| 563 | -static struct platform_device *sdp3430_devices[] __initdata = { |
| 564 | - &sdp3430_lcd_device, |
| 565 | +static struct regulator_consumer_supply sdp3430_vdda_dac_supply = { |
| 566 | + .supply = "vdda_dac", |
| 567 | + .dev = &sdp3430_dss_device.dev, |
| 568 | }; |
| 569 | |
| 570 | -static struct omap_lcd_config sdp3430_lcd_config __initdata = { |
| 571 | - .ctrl_name = "internal", |
| 572 | +static struct platform_device *sdp3430_devices[] __initdata = { |
| 573 | + &sdp3430_dss_device, |
| 574 | }; |
| 575 | |
| 576 | static struct omap_board_config_kernel sdp3430_config[] __initdata = { |
| 577 | - { OMAP_TAG_LCD, &sdp3430_lcd_config }, |
| 578 | }; |
| 579 | |
| 580 | static void __init omap_3430sdp_init_irq(void) |
| 581 | @@ -392,22 +514,34 @@ static struct regulator_init_data sdp343 |
| 582 | | REGULATOR_CHANGE_STATUS, |
| 583 | }, |
| 584 | .num_consumer_supplies = 1, |
| 585 | - .consumer_supplies = &sdp3430_vdac_supply, |
| 586 | + .consumer_supplies = &sdp3430_vdda_dac_supply, |
| 587 | }; |
| 588 | |
| 589 | /* VPLL2 for digital video outputs */ |
| 590 | +static struct regulator_consumer_supply sdp3430_vpll2_supplies[] = { |
| 591 | + { |
| 592 | + .supply = "vdvi", |
| 593 | + .dev = &sdp3430_lcd_device.dev, |
| 594 | + }, |
| 595 | + { |
| 596 | + .supply = "vdds_dsi", |
| 597 | + .dev = &sdp3430_dss_device.dev, |
| 598 | + } |
| 599 | +}; |
| 600 | + |
| 601 | static struct regulator_init_data sdp3430_vpll2 = { |
| 602 | .constraints = { |
| 603 | .name = "VDVI", |
| 604 | .min_uV = 1800000, |
| 605 | .max_uV = 1800000, |
| 606 | + .apply_uV = true, |
| 607 | .valid_modes_mask = REGULATOR_MODE_NORMAL |
| 608 | | REGULATOR_MODE_STANDBY, |
| 609 | .valid_ops_mask = REGULATOR_CHANGE_MODE |
| 610 | | REGULATOR_CHANGE_STATUS, |
| 611 | }, |
| 612 | - .num_consumer_supplies = 1, |
| 613 | - .consumer_supplies = &sdp3430_vdvi_supply, |
| 614 | + .num_consumer_supplies = ARRAY_SIZE(sdp3430_vpll2_supplies), |
| 615 | + .consumer_supplies = sdp3430_vpll2_supplies, |
| 616 | }; |
| 617 | |
| 618 | static struct twl4030_platform_data sdp3430_twldata = { |
| 619 | @@ -499,6 +633,7 @@ static void __init omap_3430sdp_init(voi |
| 620 | omap_serial_init(); |
| 621 | usb_musb_init(); |
| 622 | board_smc91x_init(); |
| 623 | + sdp3430_display_init(); |
| 624 | enable_board_wakeup_source(); |
| 625 | } |
| 626 | |
| 627 | --- a/arch/arm/mach-omap2/clock24xx.c |
| 628 | +++ b/arch/arm/mach-omap2/clock24xx.c |
| 629 | @@ -116,10 +116,10 @@ static struct omap_clk omap24xx_clks[] = |
| 630 | CLK(NULL, "mdm_ick", &mdm_ick, CK_243X), |
| 631 | CLK(NULL, "mdm_osc_ck", &mdm_osc_ck, CK_243X), |
| 632 | /* DSS domain clocks */ |
| 633 | - CLK("omapfb", "ick", &dss_ick, CK_243X | CK_242X), |
| 634 | - CLK("omapfb", "dss1_fck", &dss1_fck, CK_243X | CK_242X), |
| 635 | - CLK("omapfb", "dss2_fck", &dss2_fck, CK_243X | CK_242X), |
| 636 | - CLK("omapfb", "tv_fck", &dss_54m_fck, CK_243X | CK_242X), |
| 637 | + CLK("omapdss", "ick", &dss_ick, CK_243X | CK_242X), |
| 638 | + CLK("omapdss", "dss1_fck", &dss1_fck, CK_243X | CK_242X), |
| 639 | + CLK("omapdss", "dss2_fck", &dss2_fck, CK_243X | CK_242X), |
| 640 | + CLK("omapdss", "tv_fck", &dss_54m_fck, CK_243X | CK_242X), |
| 641 | /* L3 domain clocks */ |
| 642 | CLK(NULL, "core_l3_ck", &core_l3_ck, CK_243X | CK_242X), |
| 643 | CLK(NULL, "ssi_fck", &ssi_ssr_sst_fck, CK_243X | CK_242X), |
| 644 | --- a/arch/arm/mach-omap2/clock34xx.c |
| 645 | +++ b/arch/arm/mach-omap2/clock34xx.c |
| 646 | @@ -236,13 +236,13 @@ static struct omap_clk omap34xx_clks[] = |
| 647 | CLK("omap_rng", "ick", &rng_ick, CK_343X), |
| 648 | CLK(NULL, "sha11_ick", &sha11_ick, CK_343X), |
| 649 | CLK(NULL, "des1_ick", &des1_ick, CK_343X), |
| 650 | - CLK("omapfb", "dss1_fck", &dss1_alwon_fck_3430es1, CK_3430ES1), |
| 651 | - CLK("omapfb", "dss1_fck", &dss1_alwon_fck_3430es2, CK_3430ES2), |
| 652 | - CLK("omapfb", "tv_fck", &dss_tv_fck, CK_343X), |
| 653 | - CLK("omapfb", "video_fck", &dss_96m_fck, CK_343X), |
| 654 | - CLK("omapfb", "dss2_fck", &dss2_alwon_fck, CK_343X), |
| 655 | - CLK("omapfb", "ick", &dss_ick_3430es1, CK_3430ES1), |
| 656 | - CLK("omapfb", "ick", &dss_ick_3430es2, CK_3430ES2), |
| 657 | + CLK("omapdss", "dss1_fck", &dss1_alwon_fck_3430es1, CK_3430ES1), |
| 658 | + CLK("omapdss", "dss1_fck", &dss1_alwon_fck_3430es2, CK_3430ES2), |
| 659 | + CLK("omapdss", "tv_fck", &dss_tv_fck, CK_343X), |
| 660 | + CLK("omapdss", "video_fck", &dss_96m_fck, CK_343X), |
| 661 | + CLK("omapdss", "dss2_fck", &dss2_alwon_fck, CK_343X), |
| 662 | + CLK("omapdss", "ick", &dss_ick_3430es1, CK_3430ES1), |
| 663 | + CLK("omapdss", "ick", &dss_ick_3430es2, CK_3430ES2), |
| 664 | CLK(NULL, "cam_mclk", &cam_mclk, CK_343X), |
| 665 | CLK(NULL, "cam_ick", &cam_ick, CK_343X), |
| 666 | CLK(NULL, "csi2_96m_fck", &csi2_96m_fck, CK_343X), |
| 667 | --- a/arch/arm/mach-omap2/io.c |
| 668 | +++ b/arch/arm/mach-omap2/io.c |
| 669 | @@ -22,17 +22,18 @@ |
| 670 | #include <linux/init.h> |
| 671 | #include <linux/io.h> |
| 672 | #include <linux/clk.h> |
| 673 | +#include <linux/omapfb.h> |
| 674 | |
| 675 | #include <asm/tlb.h> |
| 676 | |
| 677 | #include <asm/mach/map.h> |
| 678 | |
| 679 | #include <mach/mux.h> |
| 680 | -#include <mach/omapfb.h> |
| 681 | #include <mach/sram.h> |
| 682 | #include <mach/sdrc.h> |
| 683 | #include <mach/gpmc.h> |
| 684 | #include <mach/serial.h> |
| 685 | +#include <mach/vram.h> |
| 686 | |
| 687 | #ifndef CONFIG_ARCH_OMAP4 /* FIXME: Remove this once clkdev is ready */ |
| 688 | #include "clock.h" |
| 689 | @@ -246,6 +247,7 @@ void __init omap2_map_common_io(void) |
| 690 | omap2_check_revision(); |
| 691 | omap_sram_init(); |
| 692 | omapfb_reserve_sdram(); |
| 693 | + omap_vram_reserve_sdram(); |
| 694 | } |
| 695 | |
| 696 | /* |
| 697 | --- a/arch/arm/mach-omap2/sdrc.c |
| 698 | +++ b/arch/arm/mach-omap2/sdrc.c |
| 699 | @@ -133,3 +133,19 @@ void __init omap2_sdrc_init(struct omap_ |
| 700 | (1 << SDRC_POWER_PAGEPOLICY_SHIFT); |
| 701 | sdrc_write_reg(l, SDRC_POWER); |
| 702 | } |
| 703 | + |
| 704 | +void omap2_sms_write_rot_control(u32 val, unsigned ctx) |
| 705 | +{ |
| 706 | + sms_write_reg(val, SMS_ROT_CONTROL(ctx)); |
| 707 | +} |
| 708 | + |
| 709 | +void omap2_sms_write_rot_size(u32 val, unsigned ctx) |
| 710 | +{ |
| 711 | + sms_write_reg(val, SMS_ROT_SIZE(ctx)); |
| 712 | +} |
| 713 | + |
| 714 | +void omap2_sms_write_rot_physical_ba(u32 val, unsigned ctx) |
| 715 | +{ |
| 716 | + sms_write_reg(val, SMS_ROT_PHYSICAL_BA(ctx)); |
| 717 | +} |
| 718 | + |
| 719 | --- a/arch/arm/plat-omap/fb.c |
| 720 | +++ b/arch/arm/plat-omap/fb.c |
| 721 | @@ -28,13 +28,13 @@ |
| 722 | #include <linux/platform_device.h> |
| 723 | #include <linux/bootmem.h> |
| 724 | #include <linux/io.h> |
| 725 | +#include <linux/omapfb.h> |
| 726 | |
| 727 | #include <mach/hardware.h> |
| 728 | #include <asm/mach/map.h> |
| 729 | |
| 730 | #include <mach/board.h> |
| 731 | #include <mach/sram.h> |
| 732 | -#include <mach/omapfb.h> |
| 733 | |
| 734 | #if defined(CONFIG_FB_OMAP) || defined(CONFIG_FB_OMAP_MODULE) |
| 735 | |
| 736 | @@ -327,7 +327,33 @@ static inline int omap_init_fb(void) |
| 737 | |
| 738 | arch_initcall(omap_init_fb); |
| 739 | |
| 740 | -#else |
| 741 | +#elif defined(CONFIG_FB_OMAP2) || defined(CONFIG_FB_OMAP2_MODULE) |
| 742 | + |
| 743 | +static u64 omap_fb_dma_mask = ~(u32)0; |
| 744 | +static struct omapfb_platform_data omapfb_config; |
| 745 | + |
| 746 | +static struct platform_device omap_fb_device = { |
| 747 | + .name = "omapfb", |
| 748 | + .id = -1, |
| 749 | + .dev = { |
| 750 | + .dma_mask = &omap_fb_dma_mask, |
| 751 | + .coherent_dma_mask = ~(u32)0, |
| 752 | + .platform_data = &omapfb_config, |
| 753 | + }, |
| 754 | + .num_resources = 0, |
| 755 | +}; |
| 756 | + |
| 757 | +void omapfb_set_platform_data(struct omapfb_platform_data *data) |
| 758 | +{ |
| 759 | + omapfb_config = *data; |
| 760 | +} |
| 761 | + |
| 762 | +static inline int omap_init_fb(void) |
| 763 | +{ |
| 764 | + return platform_device_register(&omap_fb_device); |
| 765 | +} |
| 766 | + |
| 767 | +arch_initcall(omap_init_fb); |
| 768 | |
| 769 | void omapfb_reserve_sdram(void) {} |
| 770 | unsigned long omapfb_reserve_sram(unsigned long sram_pstart, |
| 771 | @@ -339,5 +365,16 @@ unsigned long omapfb_reserve_sram(unsign |
| 772 | return 0; |
| 773 | } |
| 774 | |
| 775 | +#else |
| 776 | + |
| 777 | +void omapfb_reserve_sdram(void) {} |
| 778 | +unsigned long omapfb_reserve_sram(unsigned long sram_pstart, |
| 779 | + unsigned long sram_vstart, |
| 780 | + unsigned long sram_size, |
| 781 | + unsigned long start_avail, |
| 782 | + unsigned long size_avail) |
| 783 | +{ |
| 784 | + return 0; |
| 785 | +} |
| 786 | |
| 787 | #endif |
| 788 | --- /dev/null |
| 789 | +++ b/arch/arm/plat-omap/include/mach/display.h |
| 790 | @@ -0,0 +1,540 @@ |
| 791 | +/* |
| 792 | + * linux/include/asm-arm/arch-omap/display.h |
| 793 | + * |
| 794 | + * Copyright (C) 2008 Nokia Corporation |
| 795 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 796 | + * |
| 797 | + * This program is free software; you can redistribute it and/or modify it |
| 798 | + * under the terms of the GNU General Public License version 2 as published by |
| 799 | + * the Free Software Foundation. |
| 800 | + * |
| 801 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 802 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 803 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 804 | + * more details. |
| 805 | + * |
| 806 | + * You should have received a copy of the GNU General Public License along with |
| 807 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 808 | + */ |
| 809 | + |
| 810 | +#ifndef __ASM_ARCH_OMAP_DISPLAY_H |
| 811 | +#define __ASM_ARCH_OMAP_DISPLAY_H |
| 812 | + |
| 813 | +#include <linux/list.h> |
| 814 | +#include <linux/kobject.h> |
| 815 | +#include <linux/device.h> |
| 816 | +#include <asm/atomic.h> |
| 817 | + |
| 818 | +#define DISPC_IRQ_FRAMEDONE (1 << 0) |
| 819 | +#define DISPC_IRQ_VSYNC (1 << 1) |
| 820 | +#define DISPC_IRQ_EVSYNC_EVEN (1 << 2) |
| 821 | +#define DISPC_IRQ_EVSYNC_ODD (1 << 3) |
| 822 | +#define DISPC_IRQ_ACBIAS_COUNT_STAT (1 << 4) |
| 823 | +#define DISPC_IRQ_PROG_LINE_NUM (1 << 5) |
| 824 | +#define DISPC_IRQ_GFX_FIFO_UNDERFLOW (1 << 6) |
| 825 | +#define DISPC_IRQ_GFX_END_WIN (1 << 7) |
| 826 | +#define DISPC_IRQ_PAL_GAMMA_MASK (1 << 8) |
| 827 | +#define DISPC_IRQ_OCP_ERR (1 << 9) |
| 828 | +#define DISPC_IRQ_VID1_FIFO_UNDERFLOW (1 << 10) |
| 829 | +#define DISPC_IRQ_VID1_END_WIN (1 << 11) |
| 830 | +#define DISPC_IRQ_VID2_FIFO_UNDERFLOW (1 << 12) |
| 831 | +#define DISPC_IRQ_VID2_END_WIN (1 << 13) |
| 832 | +#define DISPC_IRQ_SYNC_LOST (1 << 14) |
| 833 | +#define DISPC_IRQ_SYNC_LOST_DIGIT (1 << 15) |
| 834 | +#define DISPC_IRQ_WAKEUP (1 << 16) |
| 835 | + |
| 836 | +struct omap_dss_device; |
| 837 | +struct omap_overlay_manager; |
| 838 | + |
| 839 | +enum omap_display_type { |
| 840 | + OMAP_DISPLAY_TYPE_NONE = 0, |
| 841 | + OMAP_DISPLAY_TYPE_DPI = 1 << 0, |
| 842 | + OMAP_DISPLAY_TYPE_DBI = 1 << 1, |
| 843 | + OMAP_DISPLAY_TYPE_SDI = 1 << 2, |
| 844 | + OMAP_DISPLAY_TYPE_DSI = 1 << 3, |
| 845 | + OMAP_DISPLAY_TYPE_VENC = 1 << 4, |
| 846 | +}; |
| 847 | + |
| 848 | +enum omap_plane { |
| 849 | + OMAP_DSS_GFX = 0, |
| 850 | + OMAP_DSS_VIDEO1 = 1, |
| 851 | + OMAP_DSS_VIDEO2 = 2 |
| 852 | +}; |
| 853 | + |
| 854 | +enum omap_channel { |
| 855 | + OMAP_DSS_CHANNEL_LCD = 0, |
| 856 | + OMAP_DSS_CHANNEL_DIGIT = 1, |
| 857 | +}; |
| 858 | + |
| 859 | +enum omap_color_mode { |
| 860 | + OMAP_DSS_COLOR_CLUT1 = 1 << 0, /* BITMAP 1 */ |
| 861 | + OMAP_DSS_COLOR_CLUT2 = 1 << 1, /* BITMAP 2 */ |
| 862 | + OMAP_DSS_COLOR_CLUT4 = 1 << 2, /* BITMAP 4 */ |
| 863 | + OMAP_DSS_COLOR_CLUT8 = 1 << 3, /* BITMAP 8 */ |
| 864 | + OMAP_DSS_COLOR_RGB12U = 1 << 4, /* RGB12, 16-bit container */ |
| 865 | + OMAP_DSS_COLOR_ARGB16 = 1 << 5, /* ARGB16 */ |
| 866 | + OMAP_DSS_COLOR_RGB16 = 1 << 6, /* RGB16 */ |
| 867 | + OMAP_DSS_COLOR_RGB24U = 1 << 7, /* RGB24, 32-bit container */ |
| 868 | + OMAP_DSS_COLOR_RGB24P = 1 << 8, /* RGB24, 24-bit container */ |
| 869 | + OMAP_DSS_COLOR_YUV2 = 1 << 9, /* YUV2 4:2:2 co-sited */ |
| 870 | + OMAP_DSS_COLOR_UYVY = 1 << 10, /* UYVY 4:2:2 co-sited */ |
| 871 | + OMAP_DSS_COLOR_ARGB32 = 1 << 11, /* ARGB32 */ |
| 872 | + OMAP_DSS_COLOR_RGBA32 = 1 << 12, /* RGBA32 */ |
| 873 | + OMAP_DSS_COLOR_RGBX32 = 1 << 13, /* RGBx32 */ |
| 874 | + |
| 875 | + OMAP_DSS_COLOR_GFX_OMAP3 = |
| 876 | + OMAP_DSS_COLOR_CLUT1 | OMAP_DSS_COLOR_CLUT2 | |
| 877 | + OMAP_DSS_COLOR_CLUT4 | OMAP_DSS_COLOR_CLUT8 | |
| 878 | + OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 | |
| 879 | + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | |
| 880 | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 | |
| 881 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32, |
| 882 | + |
| 883 | + OMAP_DSS_COLOR_VID_OMAP3 = |
| 884 | + OMAP_DSS_COLOR_RGB12U | OMAP_DSS_COLOR_ARGB16 | |
| 885 | + OMAP_DSS_COLOR_RGB16 | OMAP_DSS_COLOR_RGB24U | |
| 886 | + OMAP_DSS_COLOR_RGB24P | OMAP_DSS_COLOR_ARGB32 | |
| 887 | + OMAP_DSS_COLOR_RGBA32 | OMAP_DSS_COLOR_RGBX32 | |
| 888 | + OMAP_DSS_COLOR_YUV2 | OMAP_DSS_COLOR_UYVY, |
| 889 | +}; |
| 890 | + |
| 891 | +enum omap_lcd_display_type { |
| 892 | + OMAP_DSS_LCD_DISPLAY_STN, |
| 893 | + OMAP_DSS_LCD_DISPLAY_TFT, |
| 894 | +}; |
| 895 | + |
| 896 | +enum omap_dss_load_mode { |
| 897 | + OMAP_DSS_LOAD_CLUT_AND_FRAME = 0, |
| 898 | + OMAP_DSS_LOAD_CLUT_ONLY = 1, |
| 899 | + OMAP_DSS_LOAD_FRAME_ONLY = 2, |
| 900 | + OMAP_DSS_LOAD_CLUT_ONCE_FRAME = 3, |
| 901 | +}; |
| 902 | + |
| 903 | +enum omap_dss_trans_key_type { |
| 904 | + OMAP_DSS_COLOR_KEY_GFX_DST = 0, |
| 905 | + OMAP_DSS_COLOR_KEY_VID_SRC = 1, |
| 906 | +}; |
| 907 | + |
| 908 | +enum omap_rfbi_te_mode { |
| 909 | + OMAP_DSS_RFBI_TE_MODE_1 = 1, |
| 910 | + OMAP_DSS_RFBI_TE_MODE_2 = 2, |
| 911 | +}; |
| 912 | + |
| 913 | +enum omap_panel_config { |
| 914 | + OMAP_DSS_LCD_IVS = 1<<0, |
| 915 | + OMAP_DSS_LCD_IHS = 1<<1, |
| 916 | + OMAP_DSS_LCD_IPC = 1<<2, |
| 917 | + OMAP_DSS_LCD_IEO = 1<<3, |
| 918 | + OMAP_DSS_LCD_RF = 1<<4, |
| 919 | + OMAP_DSS_LCD_ONOFF = 1<<5, |
| 920 | + |
| 921 | + OMAP_DSS_LCD_TFT = 1<<20, |
| 922 | +}; |
| 923 | + |
| 924 | +enum omap_dss_venc_type { |
| 925 | + OMAP_DSS_VENC_TYPE_COMPOSITE, |
| 926 | + OMAP_DSS_VENC_TYPE_SVIDEO, |
| 927 | +}; |
| 928 | + |
| 929 | +enum omap_display_caps { |
| 930 | + OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE = 1 << 0, |
| 931 | +}; |
| 932 | + |
| 933 | +enum omap_dss_update_mode { |
| 934 | + OMAP_DSS_UPDATE_DISABLED = 0, |
| 935 | + OMAP_DSS_UPDATE_AUTO, |
| 936 | + OMAP_DSS_UPDATE_MANUAL, |
| 937 | +}; |
| 938 | + |
| 939 | +enum omap_dss_display_state { |
| 940 | + OMAP_DSS_DISPLAY_DISABLED = 0, |
| 941 | + OMAP_DSS_DISPLAY_ACTIVE, |
| 942 | + OMAP_DSS_DISPLAY_SUSPENDED, |
| 943 | +}; |
| 944 | + |
| 945 | +/* XXX perhaps this should be removed */ |
| 946 | +enum omap_dss_overlay_managers { |
| 947 | + OMAP_DSS_OVL_MGR_LCD, |
| 948 | + OMAP_DSS_OVL_MGR_TV, |
| 949 | +}; |
| 950 | + |
| 951 | +enum omap_dss_rotation_type { |
| 952 | + OMAP_DSS_ROT_DMA = 0, |
| 953 | + OMAP_DSS_ROT_VRFB = 1, |
| 954 | +}; |
| 955 | + |
| 956 | +enum omap_overlay_caps { |
| 957 | + OMAP_DSS_OVL_CAP_SCALE = 1 << 0, |
| 958 | + OMAP_DSS_OVL_CAP_DISPC = 1 << 1, |
| 959 | +}; |
| 960 | + |
| 961 | +enum omap_overlay_manager_caps { |
| 962 | + OMAP_DSS_OVL_MGR_CAP_DISPC = 1 << 0, |
| 963 | +}; |
| 964 | + |
| 965 | +/* RFBI */ |
| 966 | + |
| 967 | +struct rfbi_timings { |
| 968 | + int cs_on_time; |
| 969 | + int cs_off_time; |
| 970 | + int we_on_time; |
| 971 | + int we_off_time; |
| 972 | + int re_on_time; |
| 973 | + int re_off_time; |
| 974 | + int we_cycle_time; |
| 975 | + int re_cycle_time; |
| 976 | + int cs_pulse_width; |
| 977 | + int access_time; |
| 978 | + |
| 979 | + int clk_div; |
| 980 | + |
| 981 | + u32 tim[5]; /* set by rfbi_convert_timings() */ |
| 982 | + |
| 983 | + int converted; |
| 984 | +}; |
| 985 | + |
| 986 | +void omap_rfbi_write_command(const void *buf, u32 len); |
| 987 | +void omap_rfbi_read_data(void *buf, u32 len); |
| 988 | +void omap_rfbi_write_data(const void *buf, u32 len); |
| 989 | +void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width, |
| 990 | + u16 x, u16 y, |
| 991 | + u16 w, u16 h); |
| 992 | +int omap_rfbi_enable_te(bool enable, unsigned line); |
| 993 | +int omap_rfbi_setup_te(enum omap_rfbi_te_mode mode, |
| 994 | + unsigned hs_pulse_time, unsigned vs_pulse_time, |
| 995 | + int hs_pol_inv, int vs_pol_inv, int extif_div); |
| 996 | + |
| 997 | +/* DSI */ |
| 998 | +void dsi_bus_lock(void); |
| 999 | +void dsi_bus_unlock(void); |
| 1000 | +int dsi_vc_dcs_write(int channel, u8 *data, int len); |
| 1001 | +int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len); |
| 1002 | +int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen); |
| 1003 | +int dsi_vc_set_max_rx_packet_size(int channel, u16 len); |
| 1004 | +int dsi_vc_send_null(int channel); |
| 1005 | +int dsi_vc_send_bta_sync(int channel); |
| 1006 | + |
| 1007 | +/* Board specific data */ |
| 1008 | +struct omap_dss_board_info { |
| 1009 | + int (*get_last_off_on_transaction_id)(struct device *dev); |
| 1010 | + int num_devices; |
| 1011 | + struct omap_dss_device **devices; |
| 1012 | + struct omap_dss_device *default_device; |
| 1013 | +}; |
| 1014 | + |
| 1015 | +struct omap_video_timings { |
| 1016 | + /* Unit: pixels */ |
| 1017 | + u16 x_res; |
| 1018 | + /* Unit: pixels */ |
| 1019 | + u16 y_res; |
| 1020 | + /* Unit: KHz */ |
| 1021 | + u32 pixel_clock; |
| 1022 | + /* Unit: pixel clocks */ |
| 1023 | + u16 hsw; /* Horizontal synchronization pulse width */ |
| 1024 | + /* Unit: pixel clocks */ |
| 1025 | + u16 hfp; /* Horizontal front porch */ |
| 1026 | + /* Unit: pixel clocks */ |
| 1027 | + u16 hbp; /* Horizontal back porch */ |
| 1028 | + /* Unit: line clocks */ |
| 1029 | + u16 vsw; /* Vertical synchronization pulse width */ |
| 1030 | + /* Unit: line clocks */ |
| 1031 | + u16 vfp; /* Vertical front porch */ |
| 1032 | + /* Unit: line clocks */ |
| 1033 | + u16 vbp; /* Vertical back porch */ |
| 1034 | +}; |
| 1035 | + |
| 1036 | +#ifdef CONFIG_OMAP2_DSS_VENC |
| 1037 | +/* Hardcoded timings for tv modes. Venc only uses these to |
| 1038 | + * identify the mode, and does not actually use the configs |
| 1039 | + * itself. However, the configs should be something that |
| 1040 | + * a normal monitor can also show */ |
| 1041 | +const extern struct omap_video_timings omap_dss_pal_timings; |
| 1042 | +const extern struct omap_video_timings omap_dss_ntsc_timings; |
| 1043 | +#endif |
| 1044 | + |
| 1045 | +struct omap_overlay_info { |
| 1046 | + bool enabled; |
| 1047 | + |
| 1048 | + u32 paddr; |
| 1049 | + void __iomem *vaddr; |
| 1050 | + u16 screen_width; |
| 1051 | + u16 width; |
| 1052 | + u16 height; |
| 1053 | + enum omap_color_mode color_mode; |
| 1054 | + u8 rotation; |
| 1055 | + enum omap_dss_rotation_type rotation_type; |
| 1056 | + bool mirror; |
| 1057 | + |
| 1058 | + u16 pos_x; |
| 1059 | + u16 pos_y; |
| 1060 | + u16 out_width; /* if 0, out_width == width */ |
| 1061 | + u16 out_height; /* if 0, out_height == height */ |
| 1062 | + u8 global_alpha; |
| 1063 | +}; |
| 1064 | + |
| 1065 | +struct omap_overlay { |
| 1066 | + struct kobject kobj; |
| 1067 | + struct list_head list; |
| 1068 | + |
| 1069 | + /* static fields */ |
| 1070 | + const char *name; |
| 1071 | + int id; |
| 1072 | + enum omap_color_mode supported_modes; |
| 1073 | + enum omap_overlay_caps caps; |
| 1074 | + |
| 1075 | + /* dynamic fields */ |
| 1076 | + struct omap_overlay_manager *manager; |
| 1077 | + struct omap_overlay_info info; |
| 1078 | + |
| 1079 | + /* if true, info has been changed, but not applied() yet */ |
| 1080 | + bool info_dirty; |
| 1081 | + |
| 1082 | + int (*set_manager)(struct omap_overlay *ovl, |
| 1083 | + struct omap_overlay_manager *mgr); |
| 1084 | + int (*unset_manager)(struct omap_overlay *ovl); |
| 1085 | + |
| 1086 | + int (*set_overlay_info)(struct omap_overlay *ovl, |
| 1087 | + struct omap_overlay_info *info); |
| 1088 | + void (*get_overlay_info)(struct omap_overlay *ovl, |
| 1089 | + struct omap_overlay_info *info); |
| 1090 | + |
| 1091 | + int (*wait_for_go)(struct omap_overlay *ovl); |
| 1092 | +}; |
| 1093 | + |
| 1094 | +struct omap_overlay_manager_info { |
| 1095 | + u32 default_color; |
| 1096 | + |
| 1097 | + enum omap_dss_trans_key_type trans_key_type; |
| 1098 | + u32 trans_key; |
| 1099 | + bool trans_enabled; |
| 1100 | + |
| 1101 | + bool alpha_enabled; |
| 1102 | +}; |
| 1103 | + |
| 1104 | +struct omap_overlay_manager { |
| 1105 | + struct kobject kobj; |
| 1106 | + struct list_head list; |
| 1107 | + |
| 1108 | + /* static fields */ |
| 1109 | + const char *name; |
| 1110 | + int id; |
| 1111 | + enum omap_overlay_manager_caps caps; |
| 1112 | + int num_overlays; |
| 1113 | + struct omap_overlay **overlays; |
| 1114 | + enum omap_display_type supported_displays; |
| 1115 | + |
| 1116 | + /* dynamic fields */ |
| 1117 | + struct omap_dss_device *device; |
| 1118 | + struct omap_overlay_manager_info info; |
| 1119 | + |
| 1120 | + bool device_changed; |
| 1121 | + /* if true, info has been changed but not applied() yet */ |
| 1122 | + bool info_dirty; |
| 1123 | + |
| 1124 | + int (*set_device)(struct omap_overlay_manager *mgr, |
| 1125 | + struct omap_dss_device *dssdev); |
| 1126 | + int (*unset_device)(struct omap_overlay_manager *mgr); |
| 1127 | + |
| 1128 | + int (*set_manager_info)(struct omap_overlay_manager *mgr, |
| 1129 | + struct omap_overlay_manager_info *info); |
| 1130 | + void (*get_manager_info)(struct omap_overlay_manager *mgr, |
| 1131 | + struct omap_overlay_manager_info *info); |
| 1132 | + |
| 1133 | + int (*apply)(struct omap_overlay_manager *mgr); |
| 1134 | + int (*wait_for_go)(struct omap_overlay_manager *mgr); |
| 1135 | +}; |
| 1136 | + |
| 1137 | +struct omap_dss_device { |
| 1138 | + struct device dev; |
| 1139 | + |
| 1140 | + enum omap_display_type type; |
| 1141 | + |
| 1142 | + union { |
| 1143 | + struct { |
| 1144 | + u8 data_lines; |
| 1145 | + } dpi; |
| 1146 | + |
| 1147 | + struct { |
| 1148 | + u8 channel; |
| 1149 | + u8 data_lines; |
| 1150 | + } rfbi; |
| 1151 | + |
| 1152 | + struct { |
| 1153 | + u8 datapairs; |
| 1154 | + } sdi; |
| 1155 | + |
| 1156 | + struct { |
| 1157 | + u8 clk_lane; |
| 1158 | + u8 clk_pol; |
| 1159 | + u8 data1_lane; |
| 1160 | + u8 data1_pol; |
| 1161 | + u8 data2_lane; |
| 1162 | + u8 data2_pol; |
| 1163 | + unsigned long lp_clk_hz; |
| 1164 | + unsigned long ddr_clk_hz; |
| 1165 | + |
| 1166 | + bool ext_te; |
| 1167 | + u8 ext_te_gpio; |
| 1168 | + } dsi; |
| 1169 | + |
| 1170 | + struct { |
| 1171 | + enum omap_dss_venc_type type; |
| 1172 | + bool invert_polarity; |
| 1173 | + } venc; |
| 1174 | + } phy; |
| 1175 | + |
| 1176 | + struct { |
| 1177 | + struct omap_video_timings timings; |
| 1178 | + |
| 1179 | + int acbi; /* ac-bias pin transitions per interrupt */ |
| 1180 | + /* Unit: line clocks */ |
| 1181 | + int acb; /* ac-bias pin frequency */ |
| 1182 | + |
| 1183 | + enum omap_panel_config config; |
| 1184 | + |
| 1185 | + u8 recommended_bpp; |
| 1186 | + |
| 1187 | + struct omap_dss_device *ctrl; |
| 1188 | + } panel; |
| 1189 | + |
| 1190 | + struct { |
| 1191 | + u8 pixel_size; |
| 1192 | + struct rfbi_timings rfbi_timings; |
| 1193 | + struct omap_dss_device *panel; |
| 1194 | + } ctrl; |
| 1195 | + |
| 1196 | + int reset_gpio; |
| 1197 | + |
| 1198 | + int max_backlight_level; |
| 1199 | + |
| 1200 | + const char *name; |
| 1201 | + |
| 1202 | + /* used to match device to driver */ |
| 1203 | + const char *driver_name; |
| 1204 | + |
| 1205 | + void *data; |
| 1206 | + |
| 1207 | + struct omap_dss_driver *driver; |
| 1208 | + |
| 1209 | + /* helper variable for driver suspend/resume */ |
| 1210 | + bool activate_after_resume; |
| 1211 | + |
| 1212 | + enum omap_display_caps caps; |
| 1213 | + |
| 1214 | + struct omap_overlay_manager *manager; |
| 1215 | + |
| 1216 | + enum omap_dss_display_state state; |
| 1217 | + |
| 1218 | + int (*enable)(struct omap_dss_device *dssdev); |
| 1219 | + void (*disable)(struct omap_dss_device *dssdev); |
| 1220 | + |
| 1221 | + int (*suspend)(struct omap_dss_device *dssdev); |
| 1222 | + int (*resume)(struct omap_dss_device *dssdev); |
| 1223 | + |
| 1224 | + void (*get_resolution)(struct omap_dss_device *dssdev, |
| 1225 | + u16 *xres, u16 *yres); |
| 1226 | + int (*get_recommended_bpp)(struct omap_dss_device *dssdev); |
| 1227 | + |
| 1228 | + int (*check_timings)(struct omap_dss_device *dssdev, |
| 1229 | + struct omap_video_timings *timings); |
| 1230 | + void (*set_timings)(struct omap_dss_device *dssdev, |
| 1231 | + struct omap_video_timings *timings); |
| 1232 | + void (*get_timings)(struct omap_dss_device *dssdev, |
| 1233 | + struct omap_video_timings *timings); |
| 1234 | + int (*update)(struct omap_dss_device *dssdev, |
| 1235 | + u16 x, u16 y, u16 w, u16 h); |
| 1236 | + int (*sync)(struct omap_dss_device *dssdev); |
| 1237 | + int (*wait_vsync)(struct omap_dss_device *dssdev); |
| 1238 | + |
| 1239 | + int (*set_update_mode)(struct omap_dss_device *dssdev, |
| 1240 | + enum omap_dss_update_mode); |
| 1241 | + enum omap_dss_update_mode (*get_update_mode) |
| 1242 | + (struct omap_dss_device *dssdev); |
| 1243 | + |
| 1244 | + int (*enable_te)(struct omap_dss_device *dssdev, bool enable); |
| 1245 | + int (*get_te)(struct omap_dss_device *dssdev); |
| 1246 | + |
| 1247 | + u8 (*get_rotate)(struct omap_dss_device *dssdev); |
| 1248 | + int (*set_rotate)(struct omap_dss_device *dssdev, u8 rotate); |
| 1249 | + |
| 1250 | + bool (*get_mirror)(struct omap_dss_device *dssdev); |
| 1251 | + int (*set_mirror)(struct omap_dss_device *dssdev, bool enable); |
| 1252 | + |
| 1253 | + int (*run_test)(struct omap_dss_device *dssdev, int test); |
| 1254 | + int (*memory_read)(struct omap_dss_device *dssdev, |
| 1255 | + void *buf, size_t size, |
| 1256 | + u16 x, u16 y, u16 w, u16 h); |
| 1257 | + |
| 1258 | + int (*set_wss)(struct omap_dss_device *dssdev, u32 wss); |
| 1259 | + u32 (*get_wss)(struct omap_dss_device *dssdev); |
| 1260 | + |
| 1261 | + /* platform specific */ |
| 1262 | + int (*platform_enable)(struct omap_dss_device *dssdev); |
| 1263 | + void (*platform_disable)(struct omap_dss_device *dssdev); |
| 1264 | + int (*set_backlight)(struct omap_dss_device *dssdev, int level); |
| 1265 | + int (*get_backlight)(struct omap_dss_device *dssdev); |
| 1266 | +}; |
| 1267 | + |
| 1268 | +struct omap_dss_driver { |
| 1269 | + struct device_driver driver; |
| 1270 | + |
| 1271 | + int (*probe)(struct omap_dss_device *); |
| 1272 | + void (*remove)(struct omap_dss_device *); |
| 1273 | + |
| 1274 | + int (*enable)(struct omap_dss_device *display); |
| 1275 | + void (*disable)(struct omap_dss_device *display); |
| 1276 | + int (*suspend)(struct omap_dss_device *display); |
| 1277 | + int (*resume)(struct omap_dss_device *display); |
| 1278 | + int (*run_test)(struct omap_dss_device *display, int test); |
| 1279 | + |
| 1280 | + void (*setup_update)(struct omap_dss_device *dssdev, |
| 1281 | + u16 x, u16 y, u16 w, u16 h); |
| 1282 | + |
| 1283 | + int (*enable_te)(struct omap_dss_device *dssdev, bool enable); |
| 1284 | + int (*wait_for_te)(struct omap_dss_device *dssdev); |
| 1285 | + |
| 1286 | + u8 (*get_rotate)(struct omap_dss_device *dssdev); |
| 1287 | + int (*set_rotate)(struct omap_dss_device *dssdev, u8 rotate); |
| 1288 | + |
| 1289 | + bool (*get_mirror)(struct omap_dss_device *dssdev); |
| 1290 | + int (*set_mirror)(struct omap_dss_device *dssdev, bool enable); |
| 1291 | + |
| 1292 | + int (*memory_read)(struct omap_dss_device *dssdev, |
| 1293 | + void *buf, size_t size, |
| 1294 | + u16 x, u16 y, u16 w, u16 h); |
| 1295 | +}; |
| 1296 | + |
| 1297 | +int omap_dss_register_driver(struct omap_dss_driver *); |
| 1298 | +void omap_dss_unregister_driver(struct omap_dss_driver *); |
| 1299 | + |
| 1300 | +int omap_dss_register_device(struct omap_dss_device *); |
| 1301 | +void omap_dss_unregister_device(struct omap_dss_device *); |
| 1302 | + |
| 1303 | +void omap_dss_get_device(struct omap_dss_device *dssdev); |
| 1304 | +void omap_dss_put_device(struct omap_dss_device *dssdev); |
| 1305 | +#define for_each_dss_dev(d) while ((d = omap_dss_get_next_device(d)) != NULL) |
| 1306 | +struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from); |
| 1307 | +struct omap_dss_device *omap_dss_find_device(void *data, |
| 1308 | + int (*match)(struct omap_dss_device *dssdev, void *data)); |
| 1309 | + |
| 1310 | +int omap_dss_start_device(struct omap_dss_device *dssdev); |
| 1311 | +void omap_dss_stop_device(struct omap_dss_device *dssdev); |
| 1312 | + |
| 1313 | +int omap_dss_get_num_overlay_managers(void); |
| 1314 | +struct omap_overlay_manager *omap_dss_get_overlay_manager(int num); |
| 1315 | + |
| 1316 | +int omap_dss_get_num_overlays(void); |
| 1317 | +struct omap_overlay *omap_dss_get_overlay(int num); |
| 1318 | + |
| 1319 | +typedef void (*omap_dispc_isr_t) (void *arg, u32 mask); |
| 1320 | +int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask); |
| 1321 | +int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask); |
| 1322 | + |
| 1323 | +int omap_dispc_wait_for_irq_timeout(u32 irqmask, unsigned long timeout); |
| 1324 | +int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, |
| 1325 | + unsigned long timeout); |
| 1326 | + |
| 1327 | +#define to_dss_driver(x) container_of((x), struct omap_dss_driver, driver) |
| 1328 | +#define to_dss_device(x) container_of((x), struct omap_dss_device, dev) |
| 1329 | + |
| 1330 | +#endif |
| 1331 | --- a/arch/arm/plat-omap/include/mach/omapfb.h |
| 1332 | +++ /dev/null |
| 1333 | @@ -1,398 +0,0 @@ |
| 1334 | -/* |
| 1335 | - * File: arch/arm/plat-omap/include/mach/omapfb.h |
| 1336 | - * |
| 1337 | - * Framebuffer driver for TI OMAP boards |
| 1338 | - * |
| 1339 | - * Copyright (C) 2004 Nokia Corporation |
| 1340 | - * Author: Imre Deak <imre.deak@nokia.com> |
| 1341 | - * |
| 1342 | - * This program is free software; you can redistribute it and/or modify it |
| 1343 | - * under the terms of the GNU General Public License as published by the |
| 1344 | - * Free Software Foundation; either version 2 of the License, or (at your |
| 1345 | - * option) any later version. |
| 1346 | - * |
| 1347 | - * This program is distributed in the hope that it will be useful, but |
| 1348 | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 1349 | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 1350 | - * General Public License for more details. |
| 1351 | - * |
| 1352 | - * You should have received a copy of the GNU General Public License along |
| 1353 | - * with this program; if not, write to the Free Software Foundation, Inc., |
| 1354 | - * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 1355 | - */ |
| 1356 | - |
| 1357 | -#ifndef __OMAPFB_H |
| 1358 | -#define __OMAPFB_H |
| 1359 | - |
| 1360 | -#include <asm/ioctl.h> |
| 1361 | -#include <asm/types.h> |
| 1362 | - |
| 1363 | -/* IOCTL commands. */ |
| 1364 | - |
| 1365 | -#define OMAP_IOW(num, dtype) _IOW('O', num, dtype) |
| 1366 | -#define OMAP_IOR(num, dtype) _IOR('O', num, dtype) |
| 1367 | -#define OMAP_IOWR(num, dtype) _IOWR('O', num, dtype) |
| 1368 | -#define OMAP_IO(num) _IO('O', num) |
| 1369 | - |
| 1370 | -#define OMAPFB_MIRROR OMAP_IOW(31, int) |
| 1371 | -#define OMAPFB_SYNC_GFX OMAP_IO(37) |
| 1372 | -#define OMAPFB_VSYNC OMAP_IO(38) |
| 1373 | -#define OMAPFB_SET_UPDATE_MODE OMAP_IOW(40, int) |
| 1374 | -#define OMAPFB_GET_CAPS OMAP_IOR(42, struct omapfb_caps) |
| 1375 | -#define OMAPFB_GET_UPDATE_MODE OMAP_IOW(43, int) |
| 1376 | -#define OMAPFB_LCD_TEST OMAP_IOW(45, int) |
| 1377 | -#define OMAPFB_CTRL_TEST OMAP_IOW(46, int) |
| 1378 | -#define OMAPFB_UPDATE_WINDOW_OLD OMAP_IOW(47, struct omapfb_update_window_old) |
| 1379 | -#define OMAPFB_SET_COLOR_KEY OMAP_IOW(50, struct omapfb_color_key) |
| 1380 | -#define OMAPFB_GET_COLOR_KEY OMAP_IOW(51, struct omapfb_color_key) |
| 1381 | -#define OMAPFB_SETUP_PLANE OMAP_IOW(52, struct omapfb_plane_info) |
| 1382 | -#define OMAPFB_QUERY_PLANE OMAP_IOW(53, struct omapfb_plane_info) |
| 1383 | -#define OMAPFB_UPDATE_WINDOW OMAP_IOW(54, struct omapfb_update_window) |
| 1384 | -#define OMAPFB_SETUP_MEM OMAP_IOW(55, struct omapfb_mem_info) |
| 1385 | -#define OMAPFB_QUERY_MEM OMAP_IOW(56, struct omapfb_mem_info) |
| 1386 | - |
| 1387 | -#define OMAPFB_CAPS_GENERIC_MASK 0x00000fff |
| 1388 | -#define OMAPFB_CAPS_LCDC_MASK 0x00fff000 |
| 1389 | -#define OMAPFB_CAPS_PANEL_MASK 0xff000000 |
| 1390 | - |
| 1391 | -#define OMAPFB_CAPS_MANUAL_UPDATE 0x00001000 |
| 1392 | -#define OMAPFB_CAPS_TEARSYNC 0x00002000 |
| 1393 | -#define OMAPFB_CAPS_PLANE_RELOCATE_MEM 0x00004000 |
| 1394 | -#define OMAPFB_CAPS_PLANE_SCALE 0x00008000 |
| 1395 | -#define OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE 0x00010000 |
| 1396 | -#define OMAPFB_CAPS_WINDOW_SCALE 0x00020000 |
| 1397 | -#define OMAPFB_CAPS_WINDOW_OVERLAY 0x00040000 |
| 1398 | -#define OMAPFB_CAPS_WINDOW_ROTATE 0x00080000 |
| 1399 | -#define OMAPFB_CAPS_SET_BACKLIGHT 0x01000000 |
| 1400 | - |
| 1401 | -/* Values from DSP must map to lower 16-bits */ |
| 1402 | -#define OMAPFB_FORMAT_MASK 0x00ff |
| 1403 | -#define OMAPFB_FORMAT_FLAG_DOUBLE 0x0100 |
| 1404 | -#define OMAPFB_FORMAT_FLAG_TEARSYNC 0x0200 |
| 1405 | -#define OMAPFB_FORMAT_FLAG_FORCE_VSYNC 0x0400 |
| 1406 | -#define OMAPFB_FORMAT_FLAG_ENABLE_OVERLAY 0x0800 |
| 1407 | -#define OMAPFB_FORMAT_FLAG_DISABLE_OVERLAY 0x1000 |
| 1408 | - |
| 1409 | -#define OMAPFB_EVENT_READY 1 |
| 1410 | -#define OMAPFB_EVENT_DISABLED 2 |
| 1411 | - |
| 1412 | -#define OMAPFB_MEMTYPE_SDRAM 0 |
| 1413 | -#define OMAPFB_MEMTYPE_SRAM 1 |
| 1414 | -#define OMAPFB_MEMTYPE_MAX 1 |
| 1415 | - |
| 1416 | -enum omapfb_color_format { |
| 1417 | - OMAPFB_COLOR_RGB565 = 0, |
| 1418 | - OMAPFB_COLOR_YUV422, |
| 1419 | - OMAPFB_COLOR_YUV420, |
| 1420 | - OMAPFB_COLOR_CLUT_8BPP, |
| 1421 | - OMAPFB_COLOR_CLUT_4BPP, |
| 1422 | - OMAPFB_COLOR_CLUT_2BPP, |
| 1423 | - OMAPFB_COLOR_CLUT_1BPP, |
| 1424 | - OMAPFB_COLOR_RGB444, |
| 1425 | - OMAPFB_COLOR_YUY422, |
| 1426 | -}; |
| 1427 | - |
| 1428 | -struct omapfb_update_window { |
| 1429 | - __u32 x, y; |
| 1430 | - __u32 width, height; |
| 1431 | - __u32 format; |
| 1432 | - __u32 out_x, out_y; |
| 1433 | - __u32 out_width, out_height; |
| 1434 | - __u32 reserved[8]; |
| 1435 | -}; |
| 1436 | - |
| 1437 | -struct omapfb_update_window_old { |
| 1438 | - __u32 x, y; |
| 1439 | - __u32 width, height; |
| 1440 | - __u32 format; |
| 1441 | -}; |
| 1442 | - |
| 1443 | -enum omapfb_plane { |
| 1444 | - OMAPFB_PLANE_GFX = 0, |
| 1445 | - OMAPFB_PLANE_VID1, |
| 1446 | - OMAPFB_PLANE_VID2, |
| 1447 | -}; |
| 1448 | - |
| 1449 | -enum omapfb_channel_out { |
| 1450 | - OMAPFB_CHANNEL_OUT_LCD = 0, |
| 1451 | - OMAPFB_CHANNEL_OUT_DIGIT, |
| 1452 | -}; |
| 1453 | - |
| 1454 | -struct omapfb_plane_info { |
| 1455 | - __u32 pos_x; |
| 1456 | - __u32 pos_y; |
| 1457 | - __u8 enabled; |
| 1458 | - __u8 channel_out; |
| 1459 | - __u8 mirror; |
| 1460 | - __u8 reserved1; |
| 1461 | - __u32 out_width; |
| 1462 | - __u32 out_height; |
| 1463 | - __u32 reserved2[12]; |
| 1464 | -}; |
| 1465 | - |
| 1466 | -struct omapfb_mem_info { |
| 1467 | - __u32 size; |
| 1468 | - __u8 type; |
| 1469 | - __u8 reserved[3]; |
| 1470 | -}; |
| 1471 | - |
| 1472 | -struct omapfb_caps { |
| 1473 | - __u32 ctrl; |
| 1474 | - __u32 plane_color; |
| 1475 | - __u32 wnd_color; |
| 1476 | -}; |
| 1477 | - |
| 1478 | -enum omapfb_color_key_type { |
| 1479 | - OMAPFB_COLOR_KEY_DISABLED = 0, |
| 1480 | - OMAPFB_COLOR_KEY_GFX_DST, |
| 1481 | - OMAPFB_COLOR_KEY_VID_SRC, |
| 1482 | -}; |
| 1483 | - |
| 1484 | -struct omapfb_color_key { |
| 1485 | - __u8 channel_out; |
| 1486 | - __u32 background; |
| 1487 | - __u32 trans_key; |
| 1488 | - __u8 key_type; |
| 1489 | -}; |
| 1490 | - |
| 1491 | -enum omapfb_update_mode { |
| 1492 | - OMAPFB_UPDATE_DISABLED = 0, |
| 1493 | - OMAPFB_AUTO_UPDATE, |
| 1494 | - OMAPFB_MANUAL_UPDATE |
| 1495 | -}; |
| 1496 | - |
| 1497 | -#ifdef __KERNEL__ |
| 1498 | - |
| 1499 | -#include <linux/completion.h> |
| 1500 | -#include <linux/interrupt.h> |
| 1501 | -#include <linux/fb.h> |
| 1502 | -#include <linux/mutex.h> |
| 1503 | - |
| 1504 | -#include <mach/board.h> |
| 1505 | - |
| 1506 | -#define OMAP_LCDC_INV_VSYNC 0x0001 |
| 1507 | -#define OMAP_LCDC_INV_HSYNC 0x0002 |
| 1508 | -#define OMAP_LCDC_INV_PIX_CLOCK 0x0004 |
| 1509 | -#define OMAP_LCDC_INV_OUTPUT_EN 0x0008 |
| 1510 | -#define OMAP_LCDC_HSVS_RISING_EDGE 0x0010 |
| 1511 | -#define OMAP_LCDC_HSVS_OPPOSITE 0x0020 |
| 1512 | - |
| 1513 | -#define OMAP_LCDC_SIGNAL_MASK 0x003f |
| 1514 | - |
| 1515 | -#define OMAP_LCDC_PANEL_TFT 0x0100 |
| 1516 | - |
| 1517 | -#define OMAPFB_PLANE_XRES_MIN 8 |
| 1518 | -#define OMAPFB_PLANE_YRES_MIN 8 |
| 1519 | - |
| 1520 | -#ifdef CONFIG_ARCH_OMAP1 |
| 1521 | -#define OMAPFB_PLANE_NUM 1 |
| 1522 | -#else |
| 1523 | -#define OMAPFB_PLANE_NUM 3 |
| 1524 | -#endif |
| 1525 | - |
| 1526 | -struct omapfb_device; |
| 1527 | - |
| 1528 | -struct lcd_panel { |
| 1529 | - const char *name; |
| 1530 | - int config; /* TFT/STN, signal inversion */ |
| 1531 | - int bpp; /* Pixel format in fb mem */ |
| 1532 | - int data_lines; /* Lines on LCD HW interface */ |
| 1533 | - |
| 1534 | - int x_res, y_res; |
| 1535 | - int pixel_clock; /* In kHz */ |
| 1536 | - int hsw; /* Horizontal synchronization |
| 1537 | - pulse width */ |
| 1538 | - int hfp; /* Horizontal front porch */ |
| 1539 | - int hbp; /* Horizontal back porch */ |
| 1540 | - int vsw; /* Vertical synchronization |
| 1541 | - pulse width */ |
| 1542 | - int vfp; /* Vertical front porch */ |
| 1543 | - int vbp; /* Vertical back porch */ |
| 1544 | - int acb; /* ac-bias pin frequency */ |
| 1545 | - int pcd; /* pixel clock divider. |
| 1546 | - Obsolete use pixel_clock instead */ |
| 1547 | - |
| 1548 | - int (*init) (struct lcd_panel *panel, |
| 1549 | - struct omapfb_device *fbdev); |
| 1550 | - void (*cleanup) (struct lcd_panel *panel); |
| 1551 | - int (*enable) (struct lcd_panel *panel); |
| 1552 | - void (*disable) (struct lcd_panel *panel); |
| 1553 | - unsigned long (*get_caps) (struct lcd_panel *panel); |
| 1554 | - int (*set_bklight_level)(struct lcd_panel *panel, |
| 1555 | - unsigned int level); |
| 1556 | - unsigned int (*get_bklight_level)(struct lcd_panel *panel); |
| 1557 | - unsigned int (*get_bklight_max) (struct lcd_panel *panel); |
| 1558 | - int (*run_test) (struct lcd_panel *panel, int test_num); |
| 1559 | -}; |
| 1560 | - |
| 1561 | -struct extif_timings { |
| 1562 | - int cs_on_time; |
| 1563 | - int cs_off_time; |
| 1564 | - int we_on_time; |
| 1565 | - int we_off_time; |
| 1566 | - int re_on_time; |
| 1567 | - int re_off_time; |
| 1568 | - int we_cycle_time; |
| 1569 | - int re_cycle_time; |
| 1570 | - int cs_pulse_width; |
| 1571 | - int access_time; |
| 1572 | - |
| 1573 | - int clk_div; |
| 1574 | - |
| 1575 | - u32 tim[5]; /* set by extif->convert_timings */ |
| 1576 | - |
| 1577 | - int converted; |
| 1578 | -}; |
| 1579 | - |
| 1580 | -struct lcd_ctrl_extif { |
| 1581 | - int (*init) (struct omapfb_device *fbdev); |
| 1582 | - void (*cleanup) (void); |
| 1583 | - void (*get_clk_info) (u32 *clk_period, u32 *max_clk_div); |
| 1584 | - unsigned long (*get_max_tx_rate)(void); |
| 1585 | - int (*convert_timings) (struct extif_timings *timings); |
| 1586 | - void (*set_timings) (const struct extif_timings *timings); |
| 1587 | - void (*set_bits_per_cycle)(int bpc); |
| 1588 | - void (*write_command) (const void *buf, unsigned int len); |
| 1589 | - void (*read_data) (void *buf, unsigned int len); |
| 1590 | - void (*write_data) (const void *buf, unsigned int len); |
| 1591 | - void (*transfer_area) (int width, int height, |
| 1592 | - void (callback)(void * data), void *data); |
| 1593 | - int (*setup_tearsync) (unsigned pin_cnt, |
| 1594 | - unsigned hs_pulse_time, unsigned vs_pulse_time, |
| 1595 | - int hs_pol_inv, int vs_pol_inv, int div); |
| 1596 | - int (*enable_tearsync) (int enable, unsigned line); |
| 1597 | - |
| 1598 | - unsigned long max_transmit_size; |
| 1599 | -}; |
| 1600 | - |
| 1601 | -struct omapfb_notifier_block { |
| 1602 | - struct notifier_block nb; |
| 1603 | - void *data; |
| 1604 | - int plane_idx; |
| 1605 | -}; |
| 1606 | - |
| 1607 | -typedef int (*omapfb_notifier_callback_t)(struct notifier_block *, |
| 1608 | - unsigned long event, |
| 1609 | - void *fbi); |
| 1610 | - |
| 1611 | -struct omapfb_mem_region { |
| 1612 | - u32 paddr; |
| 1613 | - void __iomem *vaddr; |
| 1614 | - unsigned long size; |
| 1615 | - u8 type; /* OMAPFB_PLANE_MEM_* */ |
| 1616 | - unsigned alloc:1; /* allocated by the driver */ |
| 1617 | - unsigned map:1; /* kernel mapped by the driver */ |
| 1618 | -}; |
| 1619 | - |
| 1620 | -struct omapfb_mem_desc { |
| 1621 | - int region_cnt; |
| 1622 | - struct omapfb_mem_region region[OMAPFB_PLANE_NUM]; |
| 1623 | -}; |
| 1624 | - |
| 1625 | -struct lcd_ctrl { |
| 1626 | - const char *name; |
| 1627 | - void *data; |
| 1628 | - |
| 1629 | - int (*init) (struct omapfb_device *fbdev, |
| 1630 | - int ext_mode, |
| 1631 | - struct omapfb_mem_desc *req_md); |
| 1632 | - void (*cleanup) (void); |
| 1633 | - void (*bind_client) (struct omapfb_notifier_block *nb); |
| 1634 | - void (*get_caps) (int plane, struct omapfb_caps *caps); |
| 1635 | - int (*set_update_mode)(enum omapfb_update_mode mode); |
| 1636 | - enum omapfb_update_mode (*get_update_mode)(void); |
| 1637 | - int (*setup_plane) (int plane, int channel_out, |
| 1638 | - unsigned long offset, |
| 1639 | - int screen_width, |
| 1640 | - int pos_x, int pos_y, int width, |
| 1641 | - int height, int color_mode); |
| 1642 | - int (*set_rotate) (int angle); |
| 1643 | - int (*setup_mem) (int plane, size_t size, |
| 1644 | - int mem_type, unsigned long *paddr); |
| 1645 | - int (*mmap) (struct fb_info *info, |
| 1646 | - struct vm_area_struct *vma); |
| 1647 | - int (*set_scale) (int plane, |
| 1648 | - int orig_width, int orig_height, |
| 1649 | - int out_width, int out_height); |
| 1650 | - int (*enable_plane) (int plane, int enable); |
| 1651 | - int (*update_window) (struct fb_info *fbi, |
| 1652 | - struct omapfb_update_window *win, |
| 1653 | - void (*callback)(void *), |
| 1654 | - void *callback_data); |
| 1655 | - void (*sync) (void); |
| 1656 | - void (*suspend) (void); |
| 1657 | - void (*resume) (void); |
| 1658 | - int (*run_test) (int test_num); |
| 1659 | - int (*setcolreg) (u_int regno, u16 red, u16 green, |
| 1660 | - u16 blue, u16 transp, |
| 1661 | - int update_hw_mem); |
| 1662 | - int (*set_color_key) (struct omapfb_color_key *ck); |
| 1663 | - int (*get_color_key) (struct omapfb_color_key *ck); |
| 1664 | -}; |
| 1665 | - |
| 1666 | -enum omapfb_state { |
| 1667 | - OMAPFB_DISABLED = 0, |
| 1668 | - OMAPFB_SUSPENDED= 99, |
| 1669 | - OMAPFB_ACTIVE = 100 |
| 1670 | -}; |
| 1671 | - |
| 1672 | -struct omapfb_plane_struct { |
| 1673 | - int idx; |
| 1674 | - struct omapfb_plane_info info; |
| 1675 | - enum omapfb_color_format color_mode; |
| 1676 | - struct omapfb_device *fbdev; |
| 1677 | -}; |
| 1678 | - |
| 1679 | -struct omapfb_device { |
| 1680 | - int state; |
| 1681 | - int ext_lcdc; /* Using external |
| 1682 | - LCD controller */ |
| 1683 | - struct mutex rqueue_mutex; |
| 1684 | - |
| 1685 | - int palette_size; |
| 1686 | - u32 pseudo_palette[17]; |
| 1687 | - |
| 1688 | - struct lcd_panel *panel; /* LCD panel */ |
| 1689 | - const struct lcd_ctrl *ctrl; /* LCD controller */ |
| 1690 | - const struct lcd_ctrl *int_ctrl; /* internal LCD ctrl */ |
| 1691 | - struct lcd_ctrl_extif *ext_if; /* LCD ctrl external |
| 1692 | - interface */ |
| 1693 | - struct device *dev; |
| 1694 | - struct fb_var_screeninfo new_var; /* for mode changes */ |
| 1695 | - |
| 1696 | - struct omapfb_mem_desc mem_desc; |
| 1697 | - struct fb_info *fb_info[OMAPFB_PLANE_NUM]; |
| 1698 | -}; |
| 1699 | - |
| 1700 | -struct omapfb_platform_data { |
| 1701 | - struct omap_lcd_config lcd; |
| 1702 | - struct omapfb_mem_desc mem_desc; |
| 1703 | - void *ctrl_platform_data; |
| 1704 | -}; |
| 1705 | - |
| 1706 | -#ifdef CONFIG_ARCH_OMAP1 |
| 1707 | -extern struct lcd_ctrl omap1_lcd_ctrl; |
| 1708 | -#else |
| 1709 | -extern struct lcd_ctrl omap2_disp_ctrl; |
| 1710 | -#endif |
| 1711 | - |
| 1712 | -extern void omapfb_reserve_sdram(void); |
| 1713 | -extern void omapfb_register_panel(struct lcd_panel *panel); |
| 1714 | -extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval); |
| 1715 | -extern void omapfb_notify_clients(struct omapfb_device *fbdev, |
| 1716 | - unsigned long event); |
| 1717 | -extern int omapfb_register_client(struct omapfb_notifier_block *nb, |
| 1718 | - omapfb_notifier_callback_t callback, |
| 1719 | - void *callback_data); |
| 1720 | -extern int omapfb_unregister_client(struct omapfb_notifier_block *nb); |
| 1721 | -extern int omapfb_update_window_async(struct fb_info *fbi, |
| 1722 | - struct omapfb_update_window *win, |
| 1723 | - void (*callback)(void *), |
| 1724 | - void *callback_data); |
| 1725 | - |
| 1726 | -/* in arch/arm/plat-omap/fb.c */ |
| 1727 | -extern void omapfb_set_ctrl_platform_data(void *pdata); |
| 1728 | - |
| 1729 | -#endif /* __KERNEL__ */ |
| 1730 | - |
| 1731 | -#endif /* __OMAPFB_H */ |
| 1732 | --- a/arch/arm/plat-omap/include/mach/sdrc.h |
| 1733 | +++ b/arch/arm/plat-omap/include/mach/sdrc.h |
| 1734 | @@ -88,7 +88,10 @@ |
| 1735 | |
| 1736 | /* SMS register offsets - read/write with sms_{read,write}_reg() */ |
| 1737 | |
| 1738 | -#define SMS_SYSCONFIG 0x010 |
| 1739 | +#define SMS_SYSCONFIG 0x010 |
| 1740 | +#define SMS_ROT_CONTROL(context) (0x180 + 0x10 * context) |
| 1741 | +#define SMS_ROT_SIZE(context) (0x184 + 0x10 * context) |
| 1742 | +#define SMS_ROT_PHYSICAL_BA(context) (0x188 + 0x10 * context) |
| 1743 | /* REVISIT: fill in other SMS registers here */ |
| 1744 | |
| 1745 | |
| 1746 | @@ -121,6 +124,10 @@ int omap2_sdrc_get_params(unsigned long |
| 1747 | struct omap_sdrc_params **sdrc_cs0, |
| 1748 | struct omap_sdrc_params **sdrc_cs1); |
| 1749 | |
| 1750 | +void omap2_sms_write_rot_control(u32 val, unsigned ctx); |
| 1751 | +void omap2_sms_write_rot_size(u32 val, unsigned ctx); |
| 1752 | +void omap2_sms_write_rot_physical_ba(u32 val, unsigned ctx); |
| 1753 | + |
| 1754 | #ifdef CONFIG_ARCH_OMAP2 |
| 1755 | |
| 1756 | struct memory_timings { |
| 1757 | --- /dev/null |
| 1758 | +++ b/arch/arm/plat-omap/include/mach/vram.h |
| 1759 | @@ -0,0 +1,63 @@ |
| 1760 | +/* |
| 1761 | + * VRAM manager for OMAP |
| 1762 | + * |
| 1763 | + * Copyright (C) 2009 Nokia Corporation |
| 1764 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 1765 | + * |
| 1766 | + * This program is free software; you can redistribute it and/or modify |
| 1767 | + * it under the terms of the GNU General Public License version 2 as |
| 1768 | + * published by the Free Software Foundation. |
| 1769 | + * |
| 1770 | + * This program is distributed in the hope that it will be useful, but |
| 1771 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 1772 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 1773 | + * General Public License for more details. |
| 1774 | + * |
| 1775 | + * You should have received a copy of the GNU General Public License along |
| 1776 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 1777 | + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 1778 | + */ |
| 1779 | + |
| 1780 | +#ifndef __OMAP_VRAM_H__ |
| 1781 | +#define __OMAP_VRAM_H__ |
| 1782 | + |
| 1783 | +#include <linux/autoconf.h> |
| 1784 | +#include <linux/types.h> |
| 1785 | + |
| 1786 | +#define OMAP_VRAM_MEMTYPE_SDRAM 0 |
| 1787 | +#define OMAP_VRAM_MEMTYPE_SRAM 1 |
| 1788 | +#define OMAP_VRAM_MEMTYPE_MAX 1 |
| 1789 | + |
| 1790 | +extern int omap_vram_add_region(unsigned long paddr, size_t size); |
| 1791 | +extern int omap_vram_free(unsigned long paddr, size_t size); |
| 1792 | +extern int omap_vram_alloc(int mtype, size_t size, unsigned long *paddr); |
| 1793 | +extern int omap_vram_reserve(unsigned long paddr, size_t size); |
| 1794 | +extern void omap_vram_get_info(unsigned long *vram, unsigned long *free_vram, |
| 1795 | + unsigned long *largest_free_block); |
| 1796 | + |
| 1797 | +#ifdef CONFIG_OMAP2_VRAM |
| 1798 | +extern void omap_vram_set_sdram_vram(u32 size, u32 start); |
| 1799 | +extern void omap_vram_set_sram_vram(u32 size, u32 start); |
| 1800 | + |
| 1801 | +extern void omap_vram_reserve_sdram(void); |
| 1802 | +extern unsigned long omap_vram_reserve_sram(unsigned long sram_pstart, |
| 1803 | + unsigned long sram_vstart, |
| 1804 | + unsigned long sram_size, |
| 1805 | + unsigned long pstart_avail, |
| 1806 | + unsigned long size_avail); |
| 1807 | +#else |
| 1808 | +static inline void omap_vram_set_sdram_vram(u32 size, u32 start) { } |
| 1809 | +static inline void omap_vram_set_sram_vram(u32 size, u32 start) { } |
| 1810 | + |
| 1811 | +static inline void omap_vram_reserve_sdram(void) { } |
| 1812 | +static inline unsigned long omap_vram_reserve_sram(unsigned long sram_pstart, |
| 1813 | + unsigned long sram_vstart, |
| 1814 | + unsigned long sram_size, |
| 1815 | + unsigned long pstart_avail, |
| 1816 | + unsigned long size_avail) |
| 1817 | +{ |
| 1818 | + return 0; |
| 1819 | +} |
| 1820 | +#endif |
| 1821 | + |
| 1822 | +#endif |
| 1823 | --- /dev/null |
| 1824 | +++ b/arch/arm/plat-omap/include/mach/vrfb.h |
| 1825 | @@ -0,0 +1,46 @@ |
| 1826 | +/* |
| 1827 | + * VRFB Rotation Engine |
| 1828 | + * |
| 1829 | + * Copyright (C) 2009 Nokia Corporation |
| 1830 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 1831 | + * |
| 1832 | + * This program is free software; you can redistribute it and/or modify |
| 1833 | + * it under the terms of the GNU General Public License version 2 as |
| 1834 | + * published by the Free Software Foundation. |
| 1835 | + * |
| 1836 | + * This program is distributed in the hope that it will be useful, but |
| 1837 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 1838 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 1839 | + * General Public License for more details. |
| 1840 | + * |
| 1841 | + * You should have received a copy of the GNU General Public License along |
| 1842 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 1843 | + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 1844 | + */ |
| 1845 | + |
| 1846 | +#ifndef __OMAP_VRFB_H__ |
| 1847 | +#define __OMAP_VRFB_H__ |
| 1848 | + |
| 1849 | +#define OMAP_VRFB_LINE_LEN 2048 |
| 1850 | + |
| 1851 | +struct vrfb { |
| 1852 | + u8 context; |
| 1853 | + void __iomem *vaddr[4]; |
| 1854 | + unsigned long paddr[4]; |
| 1855 | + u16 xoffset; |
| 1856 | + u16 yoffset; |
| 1857 | + u8 bytespp; |
| 1858 | +}; |
| 1859 | + |
| 1860 | +extern int omap_vrfb_request_ctx(struct vrfb *vrfb); |
| 1861 | +extern void omap_vrfb_release_ctx(struct vrfb *vrfb); |
| 1862 | +extern void omap_vrfb_suspend_ctx(struct vrfb *vrfb); |
| 1863 | +extern void omap_vrfb_resume_ctx(struct vrfb *vrfb); |
| 1864 | +extern void omap_vrfb_adjust_size(u16 *width, u16 *height, |
| 1865 | + u8 bytespp); |
| 1866 | +extern void omap_vrfb_setup(struct vrfb *vrfb, unsigned long paddr, |
| 1867 | + u16 width, u16 height, |
| 1868 | + unsigned bytespp, bool yuv_mode); |
| 1869 | +extern void omap_vrfb_restore_context(void); |
| 1870 | + |
| 1871 | +#endif /* __VRFB_H */ |
| 1872 | --- a/arch/arm/plat-omap/sram.c |
| 1873 | +++ b/arch/arm/plat-omap/sram.c |
| 1874 | @@ -28,6 +28,7 @@ |
| 1875 | #include <mach/sram.h> |
| 1876 | #include <mach/board.h> |
| 1877 | #include <mach/cpu.h> |
| 1878 | +#include <mach/vram.h> |
| 1879 | |
| 1880 | #include <mach/control.h> |
| 1881 | |
| 1882 | @@ -185,6 +186,13 @@ void __init omap_detect_sram(void) |
| 1883 | omap_sram_start + SRAM_BOOTLOADER_SZ, |
| 1884 | omap_sram_size - SRAM_BOOTLOADER_SZ); |
| 1885 | omap_sram_size -= reserved; |
| 1886 | + |
| 1887 | + reserved = omap_vram_reserve_sram(omap_sram_start, omap_sram_base, |
| 1888 | + omap_sram_size, |
| 1889 | + omap_sram_start + SRAM_BOOTLOADER_SZ, |
| 1890 | + omap_sram_size - SRAM_BOOTLOADER_SZ); |
| 1891 | + omap_sram_size -= reserved; |
| 1892 | + |
| 1893 | omap_sram_ceil = omap_sram_base + omap_sram_size; |
| 1894 | } |
| 1895 | |
| 1896 | --- a/drivers/video/Kconfig |
| 1897 | +++ b/drivers/video/Kconfig |
| 1898 | @@ -2161,6 +2161,7 @@ config FB_BROADSHEET |
| 1899 | a bridge adapter. |
| 1900 | |
| 1901 | source "drivers/video/omap/Kconfig" |
| 1902 | +source "drivers/video/omap2/Kconfig" |
| 1903 | |
| 1904 | source "drivers/video/backlight/Kconfig" |
| 1905 | source "drivers/video/display/Kconfig" |
| 1906 | --- a/drivers/video/Makefile |
| 1907 | +++ b/drivers/video/Makefile |
| 1908 | @@ -124,6 +124,7 @@ obj-$(CONFIG_FB_SM501) += sm5 |
| 1909 | obj-$(CONFIG_FB_XILINX) += xilinxfb.o |
| 1910 | obj-$(CONFIG_FB_SH_MOBILE_LCDC) += sh_mobile_lcdcfb.o |
| 1911 | obj-$(CONFIG_FB_OMAP) += omap/ |
| 1912 | +obj-y += omap2/ |
| 1913 | obj-$(CONFIG_XEN_FBDEV_FRONTEND) += xen-fbfront.o |
| 1914 | obj-$(CONFIG_FB_CARMINE) += carminefb.o |
| 1915 | obj-$(CONFIG_FB_MB862XX) += mb862xx/ |
| 1916 | --- a/drivers/video/omap/Kconfig |
| 1917 | +++ b/drivers/video/omap/Kconfig |
| 1918 | @@ -1,6 +1,7 @@ |
| 1919 | config FB_OMAP |
| 1920 | tristate "OMAP frame buffer support (EXPERIMENTAL)" |
| 1921 | - depends on FB && ARCH_OMAP |
| 1922 | + depends on FB && ARCH_OMAP && (OMAP2_DSS = "n") |
| 1923 | + |
| 1924 | select FB_CFB_FILLRECT |
| 1925 | select FB_CFB_COPYAREA |
| 1926 | select FB_CFB_IMAGEBLIT |
| 1927 | @@ -72,7 +73,7 @@ config FB_OMAP_LCD_MIPID |
| 1928 | |
| 1929 | config FB_OMAP_BOOTLOADER_INIT |
| 1930 | bool "Check bootloader initialization" |
| 1931 | - depends on FB_OMAP |
| 1932 | + depends on FB_OMAP || FB_OMAP2 |
| 1933 | help |
| 1934 | Say Y here if you want to enable checking if the bootloader has |
| 1935 | already initialized the display controller. In this case the |
| 1936 | --- a/drivers/video/omap/blizzard.c |
| 1937 | +++ b/drivers/video/omap/blizzard.c |
| 1938 | @@ -27,9 +27,9 @@ |
| 1939 | #include <linux/clk.h> |
| 1940 | |
| 1941 | #include <mach/dma.h> |
| 1942 | -#include <mach/omapfb.h> |
| 1943 | #include <mach/blizzard.h> |
| 1944 | |
| 1945 | +#include "omapfb.h" |
| 1946 | #include "dispc.h" |
| 1947 | |
| 1948 | #define MODULE_NAME "blizzard" |
| 1949 | --- a/drivers/video/omap/dispc.c |
| 1950 | +++ b/drivers/video/omap/dispc.c |
| 1951 | @@ -24,11 +24,12 @@ |
| 1952 | #include <linux/vmalloc.h> |
| 1953 | #include <linux/clk.h> |
| 1954 | #include <linux/io.h> |
| 1955 | +#include <linux/platform_device.h> |
| 1956 | |
| 1957 | #include <mach/sram.h> |
| 1958 | -#include <mach/omapfb.h> |
| 1959 | #include <mach/board.h> |
| 1960 | |
| 1961 | +#include "omapfb.h" |
| 1962 | #include "dispc.h" |
| 1963 | |
| 1964 | #define MODULE_NAME "dispc" |
| 1965 | @@ -188,6 +189,11 @@ static struct { |
| 1966 | struct omapfb_color_key color_key; |
| 1967 | } dispc; |
| 1968 | |
| 1969 | +static struct platform_device omapdss_device = { |
| 1970 | + .name = "omapdss", |
| 1971 | + .id = -1, |
| 1972 | +}; |
| 1973 | + |
| 1974 | static void enable_lcd_clocks(int enable); |
| 1975 | |
| 1976 | static void inline dispc_write_reg(int idx, u32 val) |
| 1977 | @@ -907,20 +913,20 @@ static irqreturn_t omap_dispc_irq_handle |
| 1978 | |
| 1979 | static int get_dss_clocks(void) |
| 1980 | { |
| 1981 | - dispc.dss_ick = clk_get(dispc.fbdev->dev, "ick"); |
| 1982 | + dispc.dss_ick = clk_get(&omapdss_device.dev, "ick"); |
| 1983 | if (IS_ERR(dispc.dss_ick)) { |
| 1984 | dev_err(dispc.fbdev->dev, "can't get ick\n"); |
| 1985 | return PTR_ERR(dispc.dss_ick); |
| 1986 | } |
| 1987 | |
| 1988 | - dispc.dss1_fck = clk_get(dispc.fbdev->dev, "dss1_fck"); |
| 1989 | + dispc.dss1_fck = clk_get(&omapdss_device.dev, "dss1_fck"); |
| 1990 | if (IS_ERR(dispc.dss1_fck)) { |
| 1991 | dev_err(dispc.fbdev->dev, "can't get dss1_fck\n"); |
| 1992 | clk_put(dispc.dss_ick); |
| 1993 | return PTR_ERR(dispc.dss1_fck); |
| 1994 | } |
| 1995 | |
| 1996 | - dispc.dss_54m_fck = clk_get(dispc.fbdev->dev, "tv_fck"); |
| 1997 | + dispc.dss_54m_fck = clk_get(&omapdss_device.dev, "tv_fck"); |
| 1998 | if (IS_ERR(dispc.dss_54m_fck)) { |
| 1999 | dev_err(dispc.fbdev->dev, "can't get tv_fck\n"); |
| 2000 | clk_put(dispc.dss_ick); |
| 2001 | @@ -1371,6 +1377,12 @@ static int omap_dispc_init(struct omapfb |
| 2002 | int skip_init = 0; |
| 2003 | int i; |
| 2004 | |
| 2005 | + r = platform_device_register(&omapdss_device); |
| 2006 | + if (r) { |
| 2007 | + dev_err(fbdev->dev, "can't register omapdss device\n"); |
| 2008 | + return r; |
| 2009 | + } |
| 2010 | + |
| 2011 | memset(&dispc, 0, sizeof(dispc)); |
| 2012 | |
| 2013 | dispc.base = ioremap(DISPC_BASE, SZ_1K); |
| 2014 | @@ -1508,6 +1520,7 @@ static void omap_dispc_cleanup(void) |
| 2015 | free_irq(INT_24XX_DSS_IRQ, dispc.fbdev); |
| 2016 | put_dss_clocks(); |
| 2017 | iounmap(dispc.base); |
| 2018 | + platform_device_unregister(&omapdss_device); |
| 2019 | } |
| 2020 | |
| 2021 | const struct lcd_ctrl omap2_int_ctrl = { |
| 2022 | --- a/drivers/video/omap/hwa742.c |
| 2023 | +++ b/drivers/video/omap/hwa742.c |
| 2024 | @@ -27,8 +27,8 @@ |
| 2025 | #include <linux/clk.h> |
| 2026 | |
| 2027 | #include <mach/dma.h> |
| 2028 | -#include <mach/omapfb.h> |
| 2029 | #include <mach/hwa742.h> |
| 2030 | +#include "omapfb.h" |
| 2031 | |
| 2032 | #define HWA742_REV_CODE_REG 0x0 |
| 2033 | #define HWA742_CONFIG_REG 0x2 |
| 2034 | --- a/drivers/video/omap/lcd_2430sdp.c |
| 2035 | +++ b/drivers/video/omap/lcd_2430sdp.c |
| 2036 | @@ -28,9 +28,10 @@ |
| 2037 | #include <linux/i2c/twl4030.h> |
| 2038 | |
| 2039 | #include <mach/mux.h> |
| 2040 | -#include <mach/omapfb.h> |
| 2041 | #include <asm/mach-types.h> |
| 2042 | |
| 2043 | +#include "omapfb.h" |
| 2044 | + |
| 2045 | #define SDP2430_LCD_PANEL_BACKLIGHT_GPIO 91 |
| 2046 | #define SDP2430_LCD_PANEL_ENABLE_GPIO 154 |
| 2047 | #define SDP3430_LCD_PANEL_BACKLIGHT_GPIO 24 |
| 2048 | --- a/drivers/video/omap/lcd_ams_delta.c |
| 2049 | +++ b/drivers/video/omap/lcd_ams_delta.c |
| 2050 | @@ -27,7 +27,8 @@ |
| 2051 | |
| 2052 | #include <mach/board-ams-delta.h> |
| 2053 | #include <mach/hardware.h> |
| 2054 | -#include <mach/omapfb.h> |
| 2055 | + |
| 2056 | +#include "omapfb.h" |
| 2057 | |
| 2058 | #define AMS_DELTA_DEFAULT_CONTRAST 112 |
| 2059 | |
| 2060 | --- a/drivers/video/omap/lcd_apollon.c |
| 2061 | +++ b/drivers/video/omap/lcd_apollon.c |
| 2062 | @@ -26,7 +26,8 @@ |
| 2063 | |
| 2064 | #include <mach/gpio.h> |
| 2065 | #include <mach/mux.h> |
| 2066 | -#include <mach/omapfb.h> |
| 2067 | + |
| 2068 | +#include "omapfb.h" |
| 2069 | |
| 2070 | /* #define USE_35INCH_LCD 1 */ |
| 2071 | |
| 2072 | --- a/drivers/video/omap/lcd_h3.c |
| 2073 | +++ b/drivers/video/omap/lcd_h3.c |
| 2074 | @@ -24,7 +24,7 @@ |
| 2075 | #include <linux/i2c/tps65010.h> |
| 2076 | |
| 2077 | #include <mach/gpio.h> |
| 2078 | -#include <mach/omapfb.h> |
| 2079 | +#include "omapfb.h" |
| 2080 | |
| 2081 | #define MODULE_NAME "omapfb-lcd_h3" |
| 2082 | |
| 2083 | --- a/drivers/video/omap/lcd_h4.c |
| 2084 | +++ b/drivers/video/omap/lcd_h4.c |
| 2085 | @@ -22,7 +22,7 @@ |
| 2086 | #include <linux/module.h> |
| 2087 | #include <linux/platform_device.h> |
| 2088 | |
| 2089 | -#include <mach/omapfb.h> |
| 2090 | +#include "omapfb.h" |
| 2091 | |
| 2092 | static int h4_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) |
| 2093 | { |
| 2094 | --- a/drivers/video/omap/lcd_inn1510.c |
| 2095 | +++ b/drivers/video/omap/lcd_inn1510.c |
| 2096 | @@ -24,7 +24,7 @@ |
| 2097 | #include <linux/io.h> |
| 2098 | |
| 2099 | #include <mach/fpga.h> |
| 2100 | -#include <mach/omapfb.h> |
| 2101 | +#include "omapfb.h" |
| 2102 | |
| 2103 | static int innovator1510_panel_init(struct lcd_panel *panel, |
| 2104 | struct omapfb_device *fbdev) |
| 2105 | --- a/drivers/video/omap/lcd_inn1610.c |
| 2106 | +++ b/drivers/video/omap/lcd_inn1610.c |
| 2107 | @@ -23,7 +23,7 @@ |
| 2108 | #include <linux/platform_device.h> |
| 2109 | |
| 2110 | #include <mach/gpio.h> |
| 2111 | -#include <mach/omapfb.h> |
| 2112 | +#include "omapfb.h" |
| 2113 | |
| 2114 | #define MODULE_NAME "omapfb-lcd_h3" |
| 2115 | |
| 2116 | --- a/drivers/video/omap/lcd_ldp.c |
| 2117 | +++ b/drivers/video/omap/lcd_ldp.c |
| 2118 | @@ -28,9 +28,10 @@ |
| 2119 | |
| 2120 | #include <mach/gpio.h> |
| 2121 | #include <mach/mux.h> |
| 2122 | -#include <mach/omapfb.h> |
| 2123 | #include <asm/mach-types.h> |
| 2124 | |
| 2125 | +#include "omapfb.h" |
| 2126 | + |
| 2127 | #define LCD_PANEL_BACKLIGHT_GPIO (15 + OMAP_MAX_GPIO_LINES) |
| 2128 | #define LCD_PANEL_ENABLE_GPIO (7 + OMAP_MAX_GPIO_LINES) |
| 2129 | |
| 2130 | --- a/drivers/video/omap/lcd_mipid.c |
| 2131 | +++ b/drivers/video/omap/lcd_mipid.c |
| 2132 | @@ -23,9 +23,10 @@ |
| 2133 | #include <linux/workqueue.h> |
| 2134 | #include <linux/spi/spi.h> |
| 2135 | |
| 2136 | -#include <mach/omapfb.h> |
| 2137 | #include <mach/lcd_mipid.h> |
| 2138 | |
| 2139 | +#include "omapfb.h" |
| 2140 | + |
| 2141 | #define MIPID_MODULE_NAME "lcd_mipid" |
| 2142 | |
| 2143 | #define MIPID_CMD_READ_DISP_ID 0x04 |
| 2144 | --- a/drivers/video/omap/lcd_omap2evm.c |
| 2145 | +++ b/drivers/video/omap/lcd_omap2evm.c |
| 2146 | @@ -27,9 +27,10 @@ |
| 2147 | #include <linux/i2c/twl4030.h> |
| 2148 | |
| 2149 | #include <mach/mux.h> |
| 2150 | -#include <mach/omapfb.h> |
| 2151 | #include <asm/mach-types.h> |
| 2152 | |
| 2153 | +#include "omapfb.h" |
| 2154 | + |
| 2155 | #define LCD_PANEL_ENABLE_GPIO 154 |
| 2156 | #define LCD_PANEL_LR 128 |
| 2157 | #define LCD_PANEL_UD 129 |
| 2158 | --- a/drivers/video/omap/lcd_omap3beagle.c |
| 2159 | +++ b/drivers/video/omap/lcd_omap3beagle.c |
| 2160 | @@ -26,9 +26,10 @@ |
| 2161 | #include <linux/i2c/twl4030.h> |
| 2162 | |
| 2163 | #include <mach/mux.h> |
| 2164 | -#include <mach/omapfb.h> |
| 2165 | #include <asm/mach-types.h> |
| 2166 | |
| 2167 | +#include "omapfb.h" |
| 2168 | + |
| 2169 | #define LCD_PANEL_ENABLE_GPIO 170 |
| 2170 | |
| 2171 | static int omap3beagle_panel_init(struct lcd_panel *panel, |
| 2172 | --- a/drivers/video/omap/lcd_omap3evm.c |
| 2173 | +++ b/drivers/video/omap/lcd_omap3evm.c |
| 2174 | @@ -26,9 +26,10 @@ |
| 2175 | #include <linux/i2c/twl4030.h> |
| 2176 | |
| 2177 | #include <mach/mux.h> |
| 2178 | -#include <mach/omapfb.h> |
| 2179 | #include <asm/mach-types.h> |
| 2180 | |
| 2181 | +#include "omapfb.h" |
| 2182 | + |
| 2183 | #define LCD_PANEL_ENABLE_GPIO 153 |
| 2184 | #define LCD_PANEL_LR 2 |
| 2185 | #define LCD_PANEL_UD 3 |
| 2186 | --- a/drivers/video/omap/lcd_osk.c |
| 2187 | +++ b/drivers/video/omap/lcd_osk.c |
| 2188 | @@ -25,7 +25,7 @@ |
| 2189 | |
| 2190 | #include <mach/gpio.h> |
| 2191 | #include <mach/mux.h> |
| 2192 | -#include <mach/omapfb.h> |
| 2193 | +#include "omapfb.h" |
| 2194 | |
| 2195 | static int osk_panel_init(struct lcd_panel *panel, struct omapfb_device *fbdev) |
| 2196 | { |
| 2197 | --- a/drivers/video/omap/lcd_overo.c |
| 2198 | +++ b/drivers/video/omap/lcd_overo.c |
| 2199 | @@ -25,9 +25,10 @@ |
| 2200 | |
| 2201 | #include <mach/gpio.h> |
| 2202 | #include <mach/mux.h> |
| 2203 | -#include <mach/omapfb.h> |
| 2204 | #include <asm/mach-types.h> |
| 2205 | |
| 2206 | +#include "omapfb.h" |
| 2207 | + |
| 2208 | #define LCD_ENABLE 144 |
| 2209 | |
| 2210 | static int overo_panel_init(struct lcd_panel *panel, |
| 2211 | --- a/drivers/video/omap/lcd_palmte.c |
| 2212 | +++ b/drivers/video/omap/lcd_palmte.c |
| 2213 | @@ -24,7 +24,7 @@ |
| 2214 | #include <linux/io.h> |
| 2215 | |
| 2216 | #include <mach/fpga.h> |
| 2217 | -#include <mach/omapfb.h> |
| 2218 | +#include "omapfb.h" |
| 2219 | |
| 2220 | static int palmte_panel_init(struct lcd_panel *panel, |
| 2221 | struct omapfb_device *fbdev) |
| 2222 | --- a/drivers/video/omap/lcd_palmtt.c |
| 2223 | +++ b/drivers/video/omap/lcd_palmtt.c |
| 2224 | @@ -30,7 +30,7 @@ GPIO13 - screen blanking |
| 2225 | #include <linux/io.h> |
| 2226 | |
| 2227 | #include <mach/gpio.h> |
| 2228 | -#include <mach/omapfb.h> |
| 2229 | +#include "omapfb.h" |
| 2230 | |
| 2231 | static int palmtt_panel_init(struct lcd_panel *panel, |
| 2232 | struct omapfb_device *fbdev) |
| 2233 | --- a/drivers/video/omap/lcd_palmz71.c |
| 2234 | +++ b/drivers/video/omap/lcd_palmz71.c |
| 2235 | @@ -24,7 +24,7 @@ |
| 2236 | #include <linux/platform_device.h> |
| 2237 | #include <linux/io.h> |
| 2238 | |
| 2239 | -#include <mach/omapfb.h> |
| 2240 | +#include "omapfb.h" |
| 2241 | |
| 2242 | static int palmz71_panel_init(struct lcd_panel *panel, |
| 2243 | struct omapfb_device *fbdev) |
| 2244 | --- a/drivers/video/omap/lcdc.c |
| 2245 | +++ b/drivers/video/omap/lcdc.c |
| 2246 | @@ -30,10 +30,11 @@ |
| 2247 | #include <linux/clk.h> |
| 2248 | |
| 2249 | #include <mach/dma.h> |
| 2250 | -#include <mach/omapfb.h> |
| 2251 | |
| 2252 | #include <asm/mach-types.h> |
| 2253 | |
| 2254 | +#include "omapfb.h" |
| 2255 | + |
| 2256 | #include "lcdc.h" |
| 2257 | |
| 2258 | #define MODULE_NAME "lcdc" |
| 2259 | --- /dev/null |
| 2260 | +++ b/drivers/video/omap/omapfb.h |
| 2261 | @@ -0,0 +1,227 @@ |
| 2262 | +/* |
| 2263 | + * File: drivers/video/omap/omapfb.h |
| 2264 | + * |
| 2265 | + * Framebuffer driver for TI OMAP boards |
| 2266 | + * |
| 2267 | + * Copyright (C) 2004 Nokia Corporation |
| 2268 | + * Author: Imre Deak <imre.deak@nokia.com> |
| 2269 | + * |
| 2270 | + * This program is free software; you can redistribute it and/or modify it |
| 2271 | + * under the terms of the GNU General Public License as published by the |
| 2272 | + * Free Software Foundation; either version 2 of the License, or (at your |
| 2273 | + * option) any later version. |
| 2274 | + * |
| 2275 | + * This program is distributed in the hope that it will be useful, but |
| 2276 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 2277 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 2278 | + * General Public License for more details. |
| 2279 | + * |
| 2280 | + * You should have received a copy of the GNU General Public License along |
| 2281 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 2282 | + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 2283 | + */ |
| 2284 | + |
| 2285 | +#ifndef __OMAPFB_H |
| 2286 | +#define __OMAPFB_H |
| 2287 | + |
| 2288 | +#include <linux/fb.h> |
| 2289 | +#include <linux/mutex.h> |
| 2290 | +#include <linux/omapfb.h> |
| 2291 | + |
| 2292 | +#define OMAPFB_EVENT_READY 1 |
| 2293 | +#define OMAPFB_EVENT_DISABLED 2 |
| 2294 | + |
| 2295 | +#define OMAP_LCDC_INV_VSYNC 0x0001 |
| 2296 | +#define OMAP_LCDC_INV_HSYNC 0x0002 |
| 2297 | +#define OMAP_LCDC_INV_PIX_CLOCK 0x0004 |
| 2298 | +#define OMAP_LCDC_INV_OUTPUT_EN 0x0008 |
| 2299 | +#define OMAP_LCDC_HSVS_RISING_EDGE 0x0010 |
| 2300 | +#define OMAP_LCDC_HSVS_OPPOSITE 0x0020 |
| 2301 | + |
| 2302 | +#define OMAP_LCDC_SIGNAL_MASK 0x003f |
| 2303 | + |
| 2304 | +#define OMAP_LCDC_PANEL_TFT 0x0100 |
| 2305 | + |
| 2306 | +#define OMAPFB_PLANE_XRES_MIN 8 |
| 2307 | +#define OMAPFB_PLANE_YRES_MIN 8 |
| 2308 | + |
| 2309 | +struct omapfb_device; |
| 2310 | + |
| 2311 | +struct lcd_panel { |
| 2312 | + const char *name; |
| 2313 | + int config; /* TFT/STN, signal inversion */ |
| 2314 | + int bpp; /* Pixel format in fb mem */ |
| 2315 | + int data_lines; /* Lines on LCD HW interface */ |
| 2316 | + |
| 2317 | + int x_res, y_res; |
| 2318 | + int pixel_clock; /* In kHz */ |
| 2319 | + int hsw; /* Horizontal synchronization |
| 2320 | + pulse width */ |
| 2321 | + int hfp; /* Horizontal front porch */ |
| 2322 | + int hbp; /* Horizontal back porch */ |
| 2323 | + int vsw; /* Vertical synchronization |
| 2324 | + pulse width */ |
| 2325 | + int vfp; /* Vertical front porch */ |
| 2326 | + int vbp; /* Vertical back porch */ |
| 2327 | + int acb; /* ac-bias pin frequency */ |
| 2328 | + int pcd; /* pixel clock divider. |
| 2329 | + Obsolete use pixel_clock instead */ |
| 2330 | + |
| 2331 | + int (*init) (struct lcd_panel *panel, |
| 2332 | + struct omapfb_device *fbdev); |
| 2333 | + void (*cleanup) (struct lcd_panel *panel); |
| 2334 | + int (*enable) (struct lcd_panel *panel); |
| 2335 | + void (*disable) (struct lcd_panel *panel); |
| 2336 | + unsigned long (*get_caps) (struct lcd_panel *panel); |
| 2337 | + int (*set_bklight_level)(struct lcd_panel *panel, |
| 2338 | + unsigned int level); |
| 2339 | + unsigned int (*get_bklight_level)(struct lcd_panel *panel); |
| 2340 | + unsigned int (*get_bklight_max) (struct lcd_panel *panel); |
| 2341 | + int (*run_test) (struct lcd_panel *panel, int test_num); |
| 2342 | +}; |
| 2343 | + |
| 2344 | +struct extif_timings { |
| 2345 | + int cs_on_time; |
| 2346 | + int cs_off_time; |
| 2347 | + int we_on_time; |
| 2348 | + int we_off_time; |
| 2349 | + int re_on_time; |
| 2350 | + int re_off_time; |
| 2351 | + int we_cycle_time; |
| 2352 | + int re_cycle_time; |
| 2353 | + int cs_pulse_width; |
| 2354 | + int access_time; |
| 2355 | + |
| 2356 | + int clk_div; |
| 2357 | + |
| 2358 | + u32 tim[5]; /* set by extif->convert_timings */ |
| 2359 | + |
| 2360 | + int converted; |
| 2361 | +}; |
| 2362 | + |
| 2363 | +struct lcd_ctrl_extif { |
| 2364 | + int (*init) (struct omapfb_device *fbdev); |
| 2365 | + void (*cleanup) (void); |
| 2366 | + void (*get_clk_info) (u32 *clk_period, u32 *max_clk_div); |
| 2367 | + unsigned long (*get_max_tx_rate)(void); |
| 2368 | + int (*convert_timings) (struct extif_timings *timings); |
| 2369 | + void (*set_timings) (const struct extif_timings *timings); |
| 2370 | + void (*set_bits_per_cycle)(int bpc); |
| 2371 | + void (*write_command) (const void *buf, unsigned int len); |
| 2372 | + void (*read_data) (void *buf, unsigned int len); |
| 2373 | + void (*write_data) (const void *buf, unsigned int len); |
| 2374 | + void (*transfer_area) (int width, int height, |
| 2375 | + void (callback)(void *data), void *data); |
| 2376 | + int (*setup_tearsync) (unsigned pin_cnt, |
| 2377 | + unsigned hs_pulse_time, unsigned vs_pulse_time, |
| 2378 | + int hs_pol_inv, int vs_pol_inv, int div); |
| 2379 | + int (*enable_tearsync) (int enable, unsigned line); |
| 2380 | + |
| 2381 | + unsigned long max_transmit_size; |
| 2382 | +}; |
| 2383 | + |
| 2384 | +struct omapfb_notifier_block { |
| 2385 | + struct notifier_block nb; |
| 2386 | + void *data; |
| 2387 | + int plane_idx; |
| 2388 | +}; |
| 2389 | + |
| 2390 | +typedef int (*omapfb_notifier_callback_t)(struct notifier_block *, |
| 2391 | + unsigned long event, |
| 2392 | + void *fbi); |
| 2393 | + |
| 2394 | +struct lcd_ctrl { |
| 2395 | + const char *name; |
| 2396 | + void *data; |
| 2397 | + |
| 2398 | + int (*init) (struct omapfb_device *fbdev, |
| 2399 | + int ext_mode, |
| 2400 | + struct omapfb_mem_desc *req_md); |
| 2401 | + void (*cleanup) (void); |
| 2402 | + void (*bind_client) (struct omapfb_notifier_block *nb); |
| 2403 | + void (*get_caps) (int plane, struct omapfb_caps *caps); |
| 2404 | + int (*set_update_mode)(enum omapfb_update_mode mode); |
| 2405 | + enum omapfb_update_mode (*get_update_mode)(void); |
| 2406 | + int (*setup_plane) (int plane, int channel_out, |
| 2407 | + unsigned long offset, |
| 2408 | + int screen_width, |
| 2409 | + int pos_x, int pos_y, int width, |
| 2410 | + int height, int color_mode); |
| 2411 | + int (*set_rotate) (int angle); |
| 2412 | + int (*setup_mem) (int plane, size_t size, |
| 2413 | + int mem_type, unsigned long *paddr); |
| 2414 | + int (*mmap) (struct fb_info *info, |
| 2415 | + struct vm_area_struct *vma); |
| 2416 | + int (*set_scale) (int plane, |
| 2417 | + int orig_width, int orig_height, |
| 2418 | + int out_width, int out_height); |
| 2419 | + int (*enable_plane) (int plane, int enable); |
| 2420 | + int (*update_window) (struct fb_info *fbi, |
| 2421 | + struct omapfb_update_window *win, |
| 2422 | + void (*callback)(void *), |
| 2423 | + void *callback_data); |
| 2424 | + void (*sync) (void); |
| 2425 | + void (*suspend) (void); |
| 2426 | + void (*resume) (void); |
| 2427 | + int (*run_test) (int test_num); |
| 2428 | + int (*setcolreg) (u_int regno, u16 red, u16 green, |
| 2429 | + u16 blue, u16 transp, |
| 2430 | + int update_hw_mem); |
| 2431 | + int (*set_color_key) (struct omapfb_color_key *ck); |
| 2432 | + int (*get_color_key) (struct omapfb_color_key *ck); |
| 2433 | +}; |
| 2434 | + |
| 2435 | +enum omapfb_state { |
| 2436 | + OMAPFB_DISABLED = 0, |
| 2437 | + OMAPFB_SUSPENDED = 99, |
| 2438 | + OMAPFB_ACTIVE = 100 |
| 2439 | +}; |
| 2440 | + |
| 2441 | +struct omapfb_plane_struct { |
| 2442 | + int idx; |
| 2443 | + struct omapfb_plane_info info; |
| 2444 | + enum omapfb_color_format color_mode; |
| 2445 | + struct omapfb_device *fbdev; |
| 2446 | +}; |
| 2447 | + |
| 2448 | +struct omapfb_device { |
| 2449 | + int state; |
| 2450 | + int ext_lcdc; /* Using external |
| 2451 | + LCD controller */ |
| 2452 | + struct mutex rqueue_mutex; |
| 2453 | + |
| 2454 | + int palette_size; |
| 2455 | + u32 pseudo_palette[17]; |
| 2456 | + |
| 2457 | + struct lcd_panel *panel; /* LCD panel */ |
| 2458 | + const struct lcd_ctrl *ctrl; /* LCD controller */ |
| 2459 | + const struct lcd_ctrl *int_ctrl; /* internal LCD ctrl */ |
| 2460 | + struct lcd_ctrl_extif *ext_if; /* LCD ctrl external |
| 2461 | + interface */ |
| 2462 | + struct device *dev; |
| 2463 | + struct fb_var_screeninfo new_var; /* for mode changes */ |
| 2464 | + |
| 2465 | + struct omapfb_mem_desc mem_desc; |
| 2466 | + struct fb_info *fb_info[OMAPFB_PLANE_NUM]; |
| 2467 | +}; |
| 2468 | + |
| 2469 | +#ifdef CONFIG_ARCH_OMAP1 |
| 2470 | +extern struct lcd_ctrl omap1_lcd_ctrl; |
| 2471 | +#else |
| 2472 | +extern struct lcd_ctrl omap2_disp_ctrl; |
| 2473 | +#endif |
| 2474 | + |
| 2475 | +extern void omapfb_register_panel(struct lcd_panel *panel); |
| 2476 | +extern void omapfb_write_first_pixel(struct omapfb_device *fbdev, u16 pixval); |
| 2477 | +extern void omapfb_notify_clients(struct omapfb_device *fbdev, |
| 2478 | + unsigned long event); |
| 2479 | +extern int omapfb_register_client(struct omapfb_notifier_block *nb, |
| 2480 | + omapfb_notifier_callback_t callback, |
| 2481 | + void *callback_data); |
| 2482 | +extern int omapfb_unregister_client(struct omapfb_notifier_block *nb); |
| 2483 | +extern int omapfb_update_window_async(struct fb_info *fbi, |
| 2484 | + struct omapfb_update_window *win, |
| 2485 | + void (*callback)(void *), |
| 2486 | + void *callback_data); |
| 2487 | + |
| 2488 | +#endif /* __OMAPFB_H */ |
| 2489 | --- a/drivers/video/omap/omapfb_main.c |
| 2490 | +++ b/drivers/video/omap/omapfb_main.c |
| 2491 | @@ -29,8 +29,8 @@ |
| 2492 | #include <linux/uaccess.h> |
| 2493 | |
| 2494 | #include <mach/dma.h> |
| 2495 | -#include <mach/omapfb.h> |
| 2496 | |
| 2497 | +#include "omapfb.h" |
| 2498 | #include "lcdc.h" |
| 2499 | #include "dispc.h" |
| 2500 | |
| 2501 | --- a/drivers/video/omap/rfbi.c |
| 2502 | +++ b/drivers/video/omap/rfbi.c |
| 2503 | @@ -27,8 +27,7 @@ |
| 2504 | #include <linux/clk.h> |
| 2505 | #include <linux/io.h> |
| 2506 | |
| 2507 | -#include <mach/omapfb.h> |
| 2508 | - |
| 2509 | +#include "omapfb.h" |
| 2510 | #include "dispc.h" |
| 2511 | |
| 2512 | /* To work around an RFBI transfer rate limitation */ |
| 2513 | --- a/drivers/video/omap/sossi.c |
| 2514 | +++ b/drivers/video/omap/sossi.c |
| 2515 | @@ -25,8 +25,8 @@ |
| 2516 | #include <linux/io.h> |
| 2517 | |
| 2518 | #include <mach/dma.h> |
| 2519 | -#include <mach/omapfb.h> |
| 2520 | |
| 2521 | +#include "omapfb.h" |
| 2522 | #include "lcdc.h" |
| 2523 | |
| 2524 | #define MODULE_NAME "omapfb-sossi" |
| 2525 | --- /dev/null |
| 2526 | +++ b/drivers/video/omap2/Kconfig |
| 2527 | @@ -0,0 +1,9 @@ |
| 2528 | +config OMAP2_VRAM |
| 2529 | + bool |
| 2530 | + |
| 2531 | +config OMAP2_VRFB |
| 2532 | + bool |
| 2533 | + |
| 2534 | +source "drivers/video/omap2/dss/Kconfig" |
| 2535 | +source "drivers/video/omap2/omapfb/Kconfig" |
| 2536 | +source "drivers/video/omap2/displays/Kconfig" |
| 2537 | --- /dev/null |
| 2538 | +++ b/drivers/video/omap2/Makefile |
| 2539 | @@ -0,0 +1,6 @@ |
| 2540 | +obj-$(CONFIG_OMAP2_VRAM) += vram.o |
| 2541 | +obj-$(CONFIG_OMAP2_VRFB) += vrfb.o |
| 2542 | + |
| 2543 | +obj-y += dss/ |
| 2544 | +obj-y += omapfb/ |
| 2545 | +obj-y += displays/ |
| 2546 | --- /dev/null |
| 2547 | +++ b/drivers/video/omap2/displays/Kconfig |
| 2548 | @@ -0,0 +1,28 @@ |
| 2549 | +menu "OMAP2/3 Display Device Drivers" |
| 2550 | + depends on OMAP2_DSS |
| 2551 | + |
| 2552 | +config PANEL_GENERIC |
| 2553 | + tristate "Generic Panel" |
| 2554 | + help |
| 2555 | + Generic panel driver. |
| 2556 | + Used for DVI output for Beagle and OMAP3 SDP. |
| 2557 | + |
| 2558 | +config PANEL_SAMSUNG_LTE430WQ_F0C |
| 2559 | + tristate "Samsung LTE430WQ-F0C LCD Panel" |
| 2560 | + depends on OMAP2_DSS |
| 2561 | + help |
| 2562 | + LCD Panel used on Overo Palo43 |
| 2563 | + |
| 2564 | +config PANEL_SHARP_LS037V7DW01 |
| 2565 | + tristate "Sharp LS037V7DW01 LCD Panel" |
| 2566 | + depends on OMAP2_DSS |
| 2567 | + help |
| 2568 | + LCD Panel used in TI's SDP3430 and EVM boards |
| 2569 | + |
| 2570 | +config PANEL_TAAL |
| 2571 | + tristate "Taal DSI Panel" |
| 2572 | + depends on OMAP2_DSS_DSI |
| 2573 | + help |
| 2574 | + Taal DSI command mode panel from TPO. |
| 2575 | + |
| 2576 | +endmenu |
| 2577 | --- /dev/null |
| 2578 | +++ b/drivers/video/omap2/displays/Makefile |
| 2579 | @@ -0,0 +1,5 @@ |
| 2580 | +obj-$(CONFIG_PANEL_GENERIC) += panel-generic.o |
| 2581 | +obj-$(CONFIG_PANEL_SAMSUNG_LTE430WQ_F0C) += panel-samsung-lte430wq-f0c.o |
| 2582 | +obj-$(CONFIG_PANEL_SHARP_LS037V7DW01) += panel-sharp-ls037v7dw01.o |
| 2583 | + |
| 2584 | +obj-$(CONFIG_PANEL_TAAL) += panel-taal.o |
| 2585 | --- /dev/null |
| 2586 | +++ b/drivers/video/omap2/displays/panel-generic.c |
| 2587 | @@ -0,0 +1,104 @@ |
| 2588 | +/* |
| 2589 | + * Generic panel support |
| 2590 | + * |
| 2591 | + * Copyright (C) 2008 Nokia Corporation |
| 2592 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 2593 | + * |
| 2594 | + * This program is free software; you can redistribute it and/or modify it |
| 2595 | + * under the terms of the GNU General Public License version 2 as published by |
| 2596 | + * the Free Software Foundation. |
| 2597 | + * |
| 2598 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 2599 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 2600 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 2601 | + * more details. |
| 2602 | + * |
| 2603 | + * You should have received a copy of the GNU General Public License along with |
| 2604 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 2605 | + */ |
| 2606 | + |
| 2607 | +#include <linux/module.h> |
| 2608 | +#include <linux/delay.h> |
| 2609 | + |
| 2610 | +#include <mach/display.h> |
| 2611 | + |
| 2612 | +static struct omap_video_timings generic_panel_timings = { |
| 2613 | + /* 640 x 480 @ 60 Hz Reduced blanking VESA CVT 0.31M3-R */ |
| 2614 | + .x_res = 640, |
| 2615 | + .y_res = 480, |
| 2616 | + .pixel_clock = 23500, |
| 2617 | + .hfp = 48, |
| 2618 | + .hsw = 32, |
| 2619 | + .hbp = 80, |
| 2620 | + .vfp = 3, |
| 2621 | + .vsw = 4, |
| 2622 | + .vbp = 7, |
| 2623 | +}; |
| 2624 | + |
| 2625 | +static int generic_panel_probe(struct omap_dss_device *dssdev) |
| 2626 | +{ |
| 2627 | + dssdev->panel.config = OMAP_DSS_LCD_TFT; |
| 2628 | + dssdev->panel.timings = generic_panel_timings; |
| 2629 | + |
| 2630 | + return 0; |
| 2631 | +} |
| 2632 | + |
| 2633 | +static void generic_panel_remove(struct omap_dss_device *dssdev) |
| 2634 | +{ |
| 2635 | +} |
| 2636 | + |
| 2637 | +static int generic_panel_enable(struct omap_dss_device *dssdev) |
| 2638 | +{ |
| 2639 | + int r = 0; |
| 2640 | + |
| 2641 | + if (dssdev->platform_enable) |
| 2642 | + r = dssdev->platform_enable(dssdev); |
| 2643 | + |
| 2644 | + return r; |
| 2645 | +} |
| 2646 | + |
| 2647 | +static void generic_panel_disable(struct omap_dss_device *dssdev) |
| 2648 | +{ |
| 2649 | + if (dssdev->platform_disable) |
| 2650 | + dssdev->platform_disable(dssdev); |
| 2651 | +} |
| 2652 | + |
| 2653 | +static int generic_panel_suspend(struct omap_dss_device *dssdev) |
| 2654 | +{ |
| 2655 | + generic_panel_disable(dssdev); |
| 2656 | + return 0; |
| 2657 | +} |
| 2658 | + |
| 2659 | +static int generic_panel_resume(struct omap_dss_device *dssdev) |
| 2660 | +{ |
| 2661 | + return generic_panel_enable(dssdev); |
| 2662 | +} |
| 2663 | + |
| 2664 | +static struct omap_dss_driver generic_driver = { |
| 2665 | + .probe = generic_panel_probe, |
| 2666 | + .remove = generic_panel_remove, |
| 2667 | + |
| 2668 | + .enable = generic_panel_enable, |
| 2669 | + .disable = generic_panel_disable, |
| 2670 | + .suspend = generic_panel_suspend, |
| 2671 | + .resume = generic_panel_resume, |
| 2672 | + |
| 2673 | + .driver = { |
| 2674 | + .name = "generic_panel", |
| 2675 | + .owner = THIS_MODULE, |
| 2676 | + }, |
| 2677 | +}; |
| 2678 | + |
| 2679 | +static int __init generic_panel_drv_init(void) |
| 2680 | +{ |
| 2681 | + return omap_dss_register_driver(&generic_driver); |
| 2682 | +} |
| 2683 | + |
| 2684 | +static void __exit generic_panel_drv_exit(void) |
| 2685 | +{ |
| 2686 | + omap_dss_unregister_driver(&generic_driver); |
| 2687 | +} |
| 2688 | + |
| 2689 | +module_init(generic_panel_drv_init); |
| 2690 | +module_exit(generic_panel_drv_exit); |
| 2691 | +MODULE_LICENSE("GPL"); |
| 2692 | --- /dev/null |
| 2693 | +++ b/drivers/video/omap2/displays/panel-samsung-lte430wq-f0c.c |
| 2694 | @@ -0,0 +1,113 @@ |
| 2695 | +/* |
| 2696 | + * LCD panel driver for Samsung LTE430WQ-F0C |
| 2697 | + * |
| 2698 | + * Author: Steve Sakoman <steve@sakoman.com> |
| 2699 | + * |
| 2700 | + * This program is free software; you can redistribute it and/or modify it |
| 2701 | + * under the terms of the GNU General Public License version 2 as published by |
| 2702 | + * the Free Software Foundation. |
| 2703 | + * |
| 2704 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 2705 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 2706 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 2707 | + * more details. |
| 2708 | + * |
| 2709 | + * You should have received a copy of the GNU General Public License along with |
| 2710 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 2711 | + */ |
| 2712 | + |
| 2713 | +#include <linux/module.h> |
| 2714 | +#include <linux/delay.h> |
| 2715 | + |
| 2716 | +#include <mach/display.h> |
| 2717 | + |
| 2718 | +static struct omap_video_timings samsung_lte_timings = { |
| 2719 | + .x_res = 480, |
| 2720 | + .y_res = 272, |
| 2721 | + |
| 2722 | + .pixel_clock = 9200, |
| 2723 | + |
| 2724 | + .hsw = 41, |
| 2725 | + .hfp = 8, |
| 2726 | + .hbp = 45-41, |
| 2727 | + |
| 2728 | + .vsw = 10, |
| 2729 | + .vfp = 4, |
| 2730 | + .vbp = 12-10, |
| 2731 | +}; |
| 2732 | + |
| 2733 | +static int samsung_lte_panel_probe(struct omap_dss_device *dssdev) |
| 2734 | +{ |
| 2735 | + dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | |
| 2736 | + OMAP_DSS_LCD_IHS; |
| 2737 | + dssdev->panel.timings = samsung_lte_timings; |
| 2738 | + |
| 2739 | + return 0; |
| 2740 | +} |
| 2741 | + |
| 2742 | +static void samsung_lte_panel_remove(struct omap_dss_device *dssdev) |
| 2743 | +{ |
| 2744 | +} |
| 2745 | + |
| 2746 | +static int samsung_lte_panel_enable(struct omap_dss_device *dssdev) |
| 2747 | +{ |
| 2748 | + int r = 0; |
| 2749 | + |
| 2750 | + /* wait couple of vsyncs until enabling the LCD */ |
| 2751 | + msleep(50); |
| 2752 | + |
| 2753 | + if (dssdev->platform_enable) |
| 2754 | + r = dssdev->platform_enable(dssdev); |
| 2755 | + |
| 2756 | + return r; |
| 2757 | +} |
| 2758 | + |
| 2759 | +static void samsung_lte_panel_disable(struct omap_dss_device *dssdev) |
| 2760 | +{ |
| 2761 | + if (dssdev->platform_disable) |
| 2762 | + dssdev->platform_disable(dssdev); |
| 2763 | + |
| 2764 | + /* wait at least 5 vsyncs after disabling the LCD */ |
| 2765 | + |
| 2766 | + msleep(100); |
| 2767 | +} |
| 2768 | + |
| 2769 | +static int samsung_lte_panel_suspend(struct omap_dss_device *dssdev) |
| 2770 | +{ |
| 2771 | + samsung_lte_panel_disable(dssdev); |
| 2772 | + return 0; |
| 2773 | +} |
| 2774 | + |
| 2775 | +static int samsung_lte_panel_resume(struct omap_dss_device *dssdev) |
| 2776 | +{ |
| 2777 | + return samsung_lte_panel_enable(dssdev); |
| 2778 | +} |
| 2779 | + |
| 2780 | +static struct omap_dss_driver samsung_lte_driver = { |
| 2781 | + .probe = samsung_lte_panel_probe, |
| 2782 | + .remove = samsung_lte_panel_remove, |
| 2783 | + |
| 2784 | + .enable = samsung_lte_panel_enable, |
| 2785 | + .disable = samsung_lte_panel_disable, |
| 2786 | + .suspend = samsung_lte_panel_suspend, |
| 2787 | + .resume = samsung_lte_panel_resume, |
| 2788 | + |
| 2789 | + .driver = { |
| 2790 | + .name = "samsung_lte_panel", |
| 2791 | + .owner = THIS_MODULE, |
| 2792 | + }, |
| 2793 | +}; |
| 2794 | + |
| 2795 | +static int __init samsung_lte_panel_drv_init(void) |
| 2796 | +{ |
| 2797 | + return omap_dss_register_driver(&samsung_lte_driver); |
| 2798 | +} |
| 2799 | + |
| 2800 | +static void __exit samsung_lte_panel_drv_exit(void) |
| 2801 | +{ |
| 2802 | + omap_dss_unregister_driver(&samsung_lte_driver); |
| 2803 | +} |
| 2804 | + |
| 2805 | +module_init(samsung_lte_panel_drv_init); |
| 2806 | +module_exit(samsung_lte_panel_drv_exit); |
| 2807 | +MODULE_LICENSE("GPL"); |
| 2808 | --- /dev/null |
| 2809 | +++ b/drivers/video/omap2/displays/panel-sharp-ls037v7dw01.c |
| 2810 | @@ -0,0 +1,153 @@ |
| 2811 | +/* |
| 2812 | + * LCD panel driver for Sharp LS037V7DW01 |
| 2813 | + * |
| 2814 | + * Copyright (C) 2008 Nokia Corporation |
| 2815 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 2816 | + * |
| 2817 | + * This program is free software; you can redistribute it and/or modify it |
| 2818 | + * under the terms of the GNU General Public License version 2 as published by |
| 2819 | + * the Free Software Foundation. |
| 2820 | + * |
| 2821 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 2822 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 2823 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 2824 | + * more details. |
| 2825 | + * |
| 2826 | + * You should have received a copy of the GNU General Public License along with |
| 2827 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 2828 | + */ |
| 2829 | + |
| 2830 | +#include <linux/module.h> |
| 2831 | +#include <linux/delay.h> |
| 2832 | +#include <linux/device.h> |
| 2833 | +#include <linux/regulator/consumer.h> |
| 2834 | +#include <linux/err.h> |
| 2835 | + |
| 2836 | +#include <mach/display.h> |
| 2837 | + |
| 2838 | +struct sharp_data { |
| 2839 | + /* XXX This regulator should actually be in SDP board file, not here, |
| 2840 | + * as it doesn't actually power the LCD, but something else that |
| 2841 | + * affects the output to LCD (I think. Somebody clarify). It doesn't do |
| 2842 | + * harm here, as SDP is the only board using this currently */ |
| 2843 | + struct regulator *vdvi_reg; |
| 2844 | +}; |
| 2845 | + |
| 2846 | +static struct omap_video_timings sharp_ls_timings = { |
| 2847 | + .x_res = 480, |
| 2848 | + .y_res = 640, |
| 2849 | + |
| 2850 | + .pixel_clock = 19200, |
| 2851 | + |
| 2852 | + .hsw = 2, |
| 2853 | + .hfp = 1, |
| 2854 | + .hbp = 28, |
| 2855 | + |
| 2856 | + .vsw = 1, |
| 2857 | + .vfp = 1, |
| 2858 | + .vbp = 1, |
| 2859 | +}; |
| 2860 | + |
| 2861 | +static int sharp_ls_panel_probe(struct omap_dss_device *dssdev) |
| 2862 | +{ |
| 2863 | + struct sharp_data *sd; |
| 2864 | + |
| 2865 | + dssdev->panel.config = OMAP_DSS_LCD_TFT | OMAP_DSS_LCD_IVS | |
| 2866 | + OMAP_DSS_LCD_IHS; |
| 2867 | + dssdev->panel.acb = 0x28; |
| 2868 | + dssdev->panel.timings = sharp_ls_timings; |
| 2869 | + |
| 2870 | + sd = kzalloc(sizeof(*sd), GFP_KERNEL); |
| 2871 | + if (!sd) |
| 2872 | + return -ENOMEM; |
| 2873 | + |
| 2874 | + dev_set_drvdata(&dssdev->dev, sd); |
| 2875 | + |
| 2876 | + sd->vdvi_reg = regulator_get(&dssdev->dev, "vdvi"); |
| 2877 | + if (IS_ERR(sd->vdvi_reg)) { |
| 2878 | + kfree(sd); |
| 2879 | + pr_err("failed to get VDVI regulator\n"); |
| 2880 | + return PTR_ERR(sd->vdvi_reg); |
| 2881 | + } |
| 2882 | + |
| 2883 | + return 0; |
| 2884 | +} |
| 2885 | + |
| 2886 | +static void sharp_ls_panel_remove(struct omap_dss_device *dssdev) |
| 2887 | +{ |
| 2888 | + struct sharp_data *sd = dev_get_drvdata(&dssdev->dev); |
| 2889 | + |
| 2890 | + regulator_put(sd->vdvi_reg); |
| 2891 | + |
| 2892 | + kfree(sd); |
| 2893 | +} |
| 2894 | + |
| 2895 | +static int sharp_ls_panel_enable(struct omap_dss_device *dssdev) |
| 2896 | +{ |
| 2897 | + struct sharp_data *sd = dev_get_drvdata(&dssdev->dev); |
| 2898 | + int r = 0; |
| 2899 | + |
| 2900 | + /* wait couple of vsyncs until enabling the LCD */ |
| 2901 | + msleep(50); |
| 2902 | + |
| 2903 | + regulator_enable(sd->vdvi_reg); |
| 2904 | + |
| 2905 | + if (dssdev->platform_enable) |
| 2906 | + r = dssdev->platform_enable(dssdev); |
| 2907 | + |
| 2908 | + return r; |
| 2909 | +} |
| 2910 | + |
| 2911 | +static void sharp_ls_panel_disable(struct omap_dss_device *dssdev) |
| 2912 | +{ |
| 2913 | + struct sharp_data *sd = dev_get_drvdata(&dssdev->dev); |
| 2914 | + |
| 2915 | + if (dssdev->platform_disable) |
| 2916 | + dssdev->platform_disable(dssdev); |
| 2917 | + |
| 2918 | + regulator_disable(sd->vdvi_reg); |
| 2919 | + |
| 2920 | + /* wait at least 5 vsyncs after disabling the LCD */ |
| 2921 | + |
| 2922 | + msleep(100); |
| 2923 | +} |
| 2924 | + |
| 2925 | +static int sharp_ls_panel_suspend(struct omap_dss_device *dssdev) |
| 2926 | +{ |
| 2927 | + sharp_ls_panel_disable(dssdev); |
| 2928 | + return 0; |
| 2929 | +} |
| 2930 | + |
| 2931 | +static int sharp_ls_panel_resume(struct omap_dss_device *dssdev) |
| 2932 | +{ |
| 2933 | + return sharp_ls_panel_enable(dssdev); |
| 2934 | +} |
| 2935 | + |
| 2936 | +static struct omap_dss_driver sharp_ls_driver = { |
| 2937 | + .probe = sharp_ls_panel_probe, |
| 2938 | + .remove = sharp_ls_panel_remove, |
| 2939 | + |
| 2940 | + .enable = sharp_ls_panel_enable, |
| 2941 | + .disable = sharp_ls_panel_disable, |
| 2942 | + .suspend = sharp_ls_panel_suspend, |
| 2943 | + .resume = sharp_ls_panel_resume, |
| 2944 | + |
| 2945 | + .driver = { |
| 2946 | + .name = "sharp_ls_panel", |
| 2947 | + .owner = THIS_MODULE, |
| 2948 | + }, |
| 2949 | +}; |
| 2950 | + |
| 2951 | +static int __init sharp_ls_panel_drv_init(void) |
| 2952 | +{ |
| 2953 | + return omap_dss_register_driver(&sharp_ls_driver); |
| 2954 | +} |
| 2955 | + |
| 2956 | +static void __exit sharp_ls_panel_drv_exit(void) |
| 2957 | +{ |
| 2958 | + omap_dss_unregister_driver(&sharp_ls_driver); |
| 2959 | +} |
| 2960 | + |
| 2961 | +module_init(sharp_ls_panel_drv_init); |
| 2962 | +module_exit(sharp_ls_panel_drv_exit); |
| 2963 | +MODULE_LICENSE("GPL"); |
| 2964 | --- /dev/null |
| 2965 | +++ b/drivers/video/omap2/displays/panel-taal.c |
| 2966 | @@ -0,0 +1,900 @@ |
| 2967 | +/* |
| 2968 | + * Taal DSI command mode panel |
| 2969 | + * |
| 2970 | + * Copyright (C) 2009 Nokia Corporation |
| 2971 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 2972 | + * |
| 2973 | + * This program is free software; you can redistribute it and/or modify it |
| 2974 | + * under the terms of the GNU General Public License version 2 as published by |
| 2975 | + * the Free Software Foundation. |
| 2976 | + * |
| 2977 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 2978 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 2979 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 2980 | + * more details. |
| 2981 | + * |
| 2982 | + * You should have received a copy of the GNU General Public License along with |
| 2983 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 2984 | + */ |
| 2985 | + |
| 2986 | +/*#define DEBUG*/ |
| 2987 | + |
| 2988 | +#include <linux/module.h> |
| 2989 | +#include <linux/delay.h> |
| 2990 | +#include <linux/err.h> |
| 2991 | +#include <linux/jiffies.h> |
| 2992 | +#include <linux/sched.h> |
| 2993 | +#include <linux/backlight.h> |
| 2994 | +#include <linux/fb.h> |
| 2995 | +#include <linux/interrupt.h> |
| 2996 | +#include <linux/gpio.h> |
| 2997 | +#include <linux/completion.h> |
| 2998 | + |
| 2999 | +#include <mach/display.h> |
| 3000 | + |
| 3001 | +/* DSI Virtual channel. Hardcoded for now. */ |
| 3002 | +#define TCH 0 |
| 3003 | + |
| 3004 | +#define DCS_READ_NUM_ERRORS 0x05 |
| 3005 | +#define DCS_READ_POWER_MODE 0x0a |
| 3006 | +#define DCS_READ_MADCTL 0x0b |
| 3007 | +#define DCS_READ_PIXEL_FORMAT 0x0c |
| 3008 | +#define DCS_SLEEP_IN 0x10 |
| 3009 | +#define DCS_SLEEP_OUT 0x11 |
| 3010 | +#define DCS_DISPLAY_OFF 0x28 |
| 3011 | +#define DCS_DISPLAY_ON 0x29 |
| 3012 | +#define DCS_COLUMN_ADDR 0x2a |
| 3013 | +#define DCS_PAGE_ADDR 0x2b |
| 3014 | +#define DCS_MEMORY_WRITE 0x2c |
| 3015 | +#define DCS_TEAR_OFF 0x34 |
| 3016 | +#define DCS_TEAR_ON 0x35 |
| 3017 | +#define DCS_MEM_ACC_CTRL 0x36 |
| 3018 | +#define DCS_PIXEL_FORMAT 0x3a |
| 3019 | +#define DCS_BRIGHTNESS 0x51 |
| 3020 | +#define DCS_CTRL_DISPLAY 0x53 |
| 3021 | +#define DCS_WRITE_CABC 0x55 |
| 3022 | +#define DCS_READ_CABC 0x56 |
| 3023 | +#define DCS_GET_ID1 0xda |
| 3024 | +#define DCS_GET_ID2 0xdb |
| 3025 | +#define DCS_GET_ID3 0xdc |
| 3026 | + |
| 3027 | +struct taal_data { |
| 3028 | + struct backlight_device *bldev; |
| 3029 | + |
| 3030 | + unsigned long hw_guard_end; /* next value of jiffies when we can |
| 3031 | + * issue the next sleep in/out command |
| 3032 | + */ |
| 3033 | + unsigned long hw_guard_wait; /* max guard time in jiffies */ |
| 3034 | + |
| 3035 | + struct omap_dss_device *dssdev; |
| 3036 | + |
| 3037 | + bool enabled; |
| 3038 | + u8 rotate; |
| 3039 | + bool mirror; |
| 3040 | + |
| 3041 | + bool te_enabled; |
| 3042 | + bool use_ext_te; |
| 3043 | + struct completion te_completion; |
| 3044 | + |
| 3045 | + bool use_dsi_bl; |
| 3046 | + |
| 3047 | + bool cabc_broken; |
| 3048 | + unsigned cabc_mode; |
| 3049 | + |
| 3050 | + bool intro_printed; |
| 3051 | +}; |
| 3052 | + |
| 3053 | +static void hw_guard_start(struct taal_data *td, int guard_msec) |
| 3054 | +{ |
| 3055 | + td->hw_guard_wait = msecs_to_jiffies(guard_msec); |
| 3056 | + td->hw_guard_end = jiffies + td->hw_guard_wait; |
| 3057 | +} |
| 3058 | + |
| 3059 | +static void hw_guard_wait(struct taal_data *td) |
| 3060 | +{ |
| 3061 | + unsigned long wait = td->hw_guard_end - jiffies; |
| 3062 | + |
| 3063 | + if ((long)wait > 0 && wait <= td->hw_guard_wait) { |
| 3064 | + set_current_state(TASK_UNINTERRUPTIBLE); |
| 3065 | + schedule_timeout(wait); |
| 3066 | + } |
| 3067 | +} |
| 3068 | + |
| 3069 | +static int taal_dcs_read_1(u8 dcs_cmd, u8 *data) |
| 3070 | +{ |
| 3071 | + int r; |
| 3072 | + u8 buf[1]; |
| 3073 | + |
| 3074 | + r = dsi_vc_dcs_read(TCH, dcs_cmd, buf, 1); |
| 3075 | + |
| 3076 | + if (r < 0) |
| 3077 | + return r; |
| 3078 | + |
| 3079 | + *data = buf[0]; |
| 3080 | + |
| 3081 | + return 0; |
| 3082 | +} |
| 3083 | + |
| 3084 | +static int taal_dcs_write_0(u8 dcs_cmd) |
| 3085 | +{ |
| 3086 | + return dsi_vc_dcs_write(TCH, &dcs_cmd, 1); |
| 3087 | +} |
| 3088 | + |
| 3089 | +static int taal_dcs_write_1(u8 dcs_cmd, u8 param) |
| 3090 | +{ |
| 3091 | + u8 buf[2]; |
| 3092 | + buf[0] = dcs_cmd; |
| 3093 | + buf[1] = param; |
| 3094 | + return dsi_vc_dcs_write(TCH, buf, 2); |
| 3095 | +} |
| 3096 | + |
| 3097 | +static int taal_sleep_in(struct taal_data *td) |
| 3098 | + |
| 3099 | +{ |
| 3100 | + u8 cmd; |
| 3101 | + int r; |
| 3102 | + |
| 3103 | + hw_guard_wait(td); |
| 3104 | + |
| 3105 | + cmd = DCS_SLEEP_IN; |
| 3106 | + r = dsi_vc_dcs_write_nosync(TCH, &cmd, 1); |
| 3107 | + if (r) |
| 3108 | + return r; |
| 3109 | + |
| 3110 | + hw_guard_start(td, 120); |
| 3111 | + |
| 3112 | + msleep(5); |
| 3113 | + |
| 3114 | + return 0; |
| 3115 | +} |
| 3116 | + |
| 3117 | +static int taal_sleep_out(struct taal_data *td) |
| 3118 | +{ |
| 3119 | + int r; |
| 3120 | + |
| 3121 | + hw_guard_wait(td); |
| 3122 | + |
| 3123 | + r = taal_dcs_write_0(DCS_SLEEP_OUT); |
| 3124 | + if (r) |
| 3125 | + return r; |
| 3126 | + |
| 3127 | + hw_guard_start(td, 120); |
| 3128 | + |
| 3129 | + msleep(5); |
| 3130 | + |
| 3131 | + return 0; |
| 3132 | +} |
| 3133 | + |
| 3134 | +static int taal_get_id(u8 *id1, u8 *id2, u8 *id3) |
| 3135 | +{ |
| 3136 | + int r; |
| 3137 | + |
| 3138 | + r = taal_dcs_read_1(DCS_GET_ID1, id1); |
| 3139 | + if (r) |
| 3140 | + return r; |
| 3141 | + r = taal_dcs_read_1(DCS_GET_ID2, id2); |
| 3142 | + if (r) |
| 3143 | + return r; |
| 3144 | + r = taal_dcs_read_1(DCS_GET_ID3, id3); |
| 3145 | + if (r) |
| 3146 | + return r; |
| 3147 | + |
| 3148 | + return 0; |
| 3149 | +} |
| 3150 | + |
| 3151 | +static int taal_set_addr_mode(u8 rotate, bool mirror) |
| 3152 | +{ |
| 3153 | + int r; |
| 3154 | + u8 mode; |
| 3155 | + int b5, b6, b7; |
| 3156 | + |
| 3157 | + r = taal_dcs_read_1(DCS_READ_MADCTL, &mode); |
| 3158 | + if (r) |
| 3159 | + return r; |
| 3160 | + |
| 3161 | + switch (rotate) { |
| 3162 | + default: |
| 3163 | + case 0: |
| 3164 | + b7 = 0; |
| 3165 | + b6 = 0; |
| 3166 | + b5 = 0; |
| 3167 | + break; |
| 3168 | + case 1: |
| 3169 | + b7 = 0; |
| 3170 | + b6 = 1; |
| 3171 | + b5 = 1; |
| 3172 | + break; |
| 3173 | + case 2: |
| 3174 | + b7 = 1; |
| 3175 | + b6 = 1; |
| 3176 | + b5 = 0; |
| 3177 | + break; |
| 3178 | + case 3: |
| 3179 | + b7 = 1; |
| 3180 | + b6 = 0; |
| 3181 | + b5 = 1; |
| 3182 | + break; |
| 3183 | + } |
| 3184 | + |
| 3185 | + if (mirror) |
| 3186 | + b6 = !b6; |
| 3187 | + |
| 3188 | + mode &= ~((1<<7) | (1<<6) | (1<<5)); |
| 3189 | + mode |= (b7 << 7) | (b6 << 6) | (b5 << 5); |
| 3190 | + |
| 3191 | + return taal_dcs_write_1(DCS_MEM_ACC_CTRL, mode); |
| 3192 | +} |
| 3193 | + |
| 3194 | +static int taal_set_update_window(u16 x, u16 y, u16 w, u16 h) |
| 3195 | +{ |
| 3196 | + int r; |
| 3197 | + u16 x1 = x; |
| 3198 | + u16 x2 = x + w - 1; |
| 3199 | + u16 y1 = y; |
| 3200 | + u16 y2 = y + h - 1; |
| 3201 | + |
| 3202 | + u8 buf[5]; |
| 3203 | + buf[0] = DCS_COLUMN_ADDR; |
| 3204 | + buf[1] = (x1 >> 8) & 0xff; |
| 3205 | + buf[2] = (x1 >> 0) & 0xff; |
| 3206 | + buf[3] = (x2 >> 8) & 0xff; |
| 3207 | + buf[4] = (x2 >> 0) & 0xff; |
| 3208 | + |
| 3209 | + r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf)); |
| 3210 | + if (r) |
| 3211 | + return r; |
| 3212 | + |
| 3213 | + buf[0] = DCS_PAGE_ADDR; |
| 3214 | + buf[1] = (y1 >> 8) & 0xff; |
| 3215 | + buf[2] = (y1 >> 0) & 0xff; |
| 3216 | + buf[3] = (y2 >> 8) & 0xff; |
| 3217 | + buf[4] = (y2 >> 0) & 0xff; |
| 3218 | + |
| 3219 | + r = dsi_vc_dcs_write_nosync(TCH, buf, sizeof(buf)); |
| 3220 | + if (r) |
| 3221 | + return r; |
| 3222 | + |
| 3223 | + dsi_vc_send_bta_sync(TCH); |
| 3224 | + |
| 3225 | + return r; |
| 3226 | +} |
| 3227 | + |
| 3228 | +static int taal_bl_update_status(struct backlight_device *dev) |
| 3229 | +{ |
| 3230 | + struct omap_dss_device *dssdev = dev_get_drvdata(&dev->dev); |
| 3231 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3232 | + int r; |
| 3233 | + int level; |
| 3234 | + |
| 3235 | + if (dev->props.fb_blank == FB_BLANK_UNBLANK && |
| 3236 | + dev->props.power == FB_BLANK_UNBLANK) |
| 3237 | + level = dev->props.brightness; |
| 3238 | + else |
| 3239 | + level = 0; |
| 3240 | + |
| 3241 | + dev_dbg(&dssdev->dev, "update brightness to %d\n", level); |
| 3242 | + |
| 3243 | + if (td->use_dsi_bl) { |
| 3244 | + if (td->enabled) { |
| 3245 | + dsi_bus_lock(); |
| 3246 | + r = taal_dcs_write_1(DCS_BRIGHTNESS, level); |
| 3247 | + dsi_bus_unlock(); |
| 3248 | + if (r) |
| 3249 | + return r; |
| 3250 | + } |
| 3251 | + } else { |
| 3252 | + if (!dssdev->set_backlight) |
| 3253 | + return -EINVAL; |
| 3254 | + |
| 3255 | + r = dssdev->set_backlight(dssdev, level); |
| 3256 | + if (r) |
| 3257 | + return r; |
| 3258 | + } |
| 3259 | + |
| 3260 | + return 0; |
| 3261 | +} |
| 3262 | + |
| 3263 | +static int taal_bl_get_intensity(struct backlight_device *dev) |
| 3264 | +{ |
| 3265 | + if (dev->props.fb_blank == FB_BLANK_UNBLANK && |
| 3266 | + dev->props.power == FB_BLANK_UNBLANK) |
| 3267 | + return dev->props.brightness; |
| 3268 | + |
| 3269 | + return 0; |
| 3270 | +} |
| 3271 | + |
| 3272 | +static struct backlight_ops taal_bl_ops = { |
| 3273 | + .get_brightness = taal_bl_get_intensity, |
| 3274 | + .update_status = taal_bl_update_status, |
| 3275 | +}; |
| 3276 | + |
| 3277 | +static void taal_get_timings(struct omap_dss_device *dssdev, |
| 3278 | + struct omap_video_timings *timings) |
| 3279 | +{ |
| 3280 | + *timings = dssdev->panel.timings; |
| 3281 | +} |
| 3282 | + |
| 3283 | +static void taal_get_resolution(struct omap_dss_device *dssdev, |
| 3284 | + u16 *xres, u16 *yres) |
| 3285 | +{ |
| 3286 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3287 | + |
| 3288 | + if (td->rotate == 0 || td->rotate == 2) { |
| 3289 | + *xres = dssdev->panel.timings.x_res; |
| 3290 | + *yres = dssdev->panel.timings.y_res; |
| 3291 | + } else { |
| 3292 | + *yres = dssdev->panel.timings.x_res; |
| 3293 | + *xres = dssdev->panel.timings.y_res; |
| 3294 | + } |
| 3295 | +} |
| 3296 | + |
| 3297 | +static irqreturn_t taal_te_isr(int irq, void *data) |
| 3298 | +{ |
| 3299 | + struct omap_dss_device *dssdev = data; |
| 3300 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3301 | + |
| 3302 | + complete_all(&td->te_completion); |
| 3303 | + |
| 3304 | + return IRQ_HANDLED; |
| 3305 | +} |
| 3306 | + |
| 3307 | +static ssize_t taal_num_errors_show(struct device *dev, |
| 3308 | + struct device_attribute *attr, char *buf) |
| 3309 | +{ |
| 3310 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 3311 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3312 | + u8 errors; |
| 3313 | + int r; |
| 3314 | + |
| 3315 | + if (td->enabled) { |
| 3316 | + dsi_bus_lock(); |
| 3317 | + r = taal_dcs_read_1(DCS_READ_NUM_ERRORS, &errors); |
| 3318 | + dsi_bus_unlock(); |
| 3319 | + } else { |
| 3320 | + r = -ENODEV; |
| 3321 | + } |
| 3322 | + |
| 3323 | + if (r) |
| 3324 | + return r; |
| 3325 | + |
| 3326 | + return snprintf(buf, PAGE_SIZE, "%d\n", errors); |
| 3327 | +} |
| 3328 | + |
| 3329 | +static ssize_t taal_hw_revision_show(struct device *dev, |
| 3330 | + struct device_attribute *attr, char *buf) |
| 3331 | +{ |
| 3332 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 3333 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3334 | + u8 id1, id2, id3; |
| 3335 | + int r; |
| 3336 | + |
| 3337 | + if (td->enabled) { |
| 3338 | + dsi_bus_lock(); |
| 3339 | + r = taal_get_id(&id1, &id2, &id3); |
| 3340 | + dsi_bus_unlock(); |
| 3341 | + } else { |
| 3342 | + r = -ENODEV; |
| 3343 | + } |
| 3344 | + |
| 3345 | + if (r) |
| 3346 | + return r; |
| 3347 | + |
| 3348 | + return snprintf(buf, PAGE_SIZE, "%02x.%02x.%02x\n", id1, id2, id3); |
| 3349 | +} |
| 3350 | + |
| 3351 | +static const char *cabc_modes[] = { |
| 3352 | + "off", /* used also always when CABC is not supported */ |
| 3353 | + "ui", |
| 3354 | + "still-image", |
| 3355 | + "moving-image", |
| 3356 | +}; |
| 3357 | + |
| 3358 | +static ssize_t show_cabc_mode(struct device *dev, |
| 3359 | + struct device_attribute *attr, |
| 3360 | + char *buf) |
| 3361 | +{ |
| 3362 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 3363 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3364 | + const char *mode_str; |
| 3365 | + int mode; |
| 3366 | + int len; |
| 3367 | + |
| 3368 | + mode = td->cabc_mode; |
| 3369 | + |
| 3370 | + mode_str = "unknown"; |
| 3371 | + if (mode >= 0 && mode < ARRAY_SIZE(cabc_modes)) |
| 3372 | + mode_str = cabc_modes[mode]; |
| 3373 | + len = snprintf(buf, PAGE_SIZE, "%s\n", mode_str); |
| 3374 | + |
| 3375 | + return len < PAGE_SIZE - 1 ? len : PAGE_SIZE - 1; |
| 3376 | +} |
| 3377 | + |
| 3378 | +static ssize_t store_cabc_mode(struct device *dev, |
| 3379 | + struct device_attribute *attr, |
| 3380 | + const char *buf, size_t count) |
| 3381 | +{ |
| 3382 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 3383 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3384 | + int i; |
| 3385 | + |
| 3386 | + for (i = 0; i < ARRAY_SIZE(cabc_modes); i++) { |
| 3387 | + if (sysfs_streq(cabc_modes[i], buf)) |
| 3388 | + break; |
| 3389 | + } |
| 3390 | + |
| 3391 | + if (i == ARRAY_SIZE(cabc_modes)) |
| 3392 | + return -EINVAL; |
| 3393 | + |
| 3394 | + if (td->enabled) { |
| 3395 | + dsi_bus_lock(); |
| 3396 | + if (!td->cabc_broken) |
| 3397 | + taal_dcs_write_1(DCS_WRITE_CABC, i); |
| 3398 | + dsi_bus_unlock(); |
| 3399 | + } |
| 3400 | + |
| 3401 | + td->cabc_mode = i; |
| 3402 | + |
| 3403 | + return count; |
| 3404 | +} |
| 3405 | + |
| 3406 | +static ssize_t show_cabc_available_modes(struct device *dev, |
| 3407 | + struct device_attribute *attr, |
| 3408 | + char *buf) |
| 3409 | +{ |
| 3410 | + int len; |
| 3411 | + int i; |
| 3412 | + |
| 3413 | + for (i = 0, len = 0; |
| 3414 | + len < PAGE_SIZE && i < ARRAY_SIZE(cabc_modes); i++) |
| 3415 | + len += snprintf(&buf[len], PAGE_SIZE - len, "%s%s%s", |
| 3416 | + i ? " " : "", cabc_modes[i], |
| 3417 | + i == ARRAY_SIZE(cabc_modes) - 1 ? "\n" : ""); |
| 3418 | + |
| 3419 | + return len < PAGE_SIZE ? len : PAGE_SIZE - 1; |
| 3420 | +} |
| 3421 | + |
| 3422 | +static DEVICE_ATTR(num_dsi_errors, S_IRUGO, taal_num_errors_show, NULL); |
| 3423 | +static DEVICE_ATTR(hw_revision, S_IRUGO, taal_hw_revision_show, NULL); |
| 3424 | +static DEVICE_ATTR(cabc_mode, S_IRUGO | S_IWUSR, |
| 3425 | + show_cabc_mode, store_cabc_mode); |
| 3426 | +static DEVICE_ATTR(cabc_available_modes, S_IRUGO, |
| 3427 | + show_cabc_available_modes, NULL); |
| 3428 | + |
| 3429 | +static struct attribute *taal_attrs[] = { |
| 3430 | + &dev_attr_num_dsi_errors.attr, |
| 3431 | + &dev_attr_hw_revision.attr, |
| 3432 | + &dev_attr_cabc_mode.attr, |
| 3433 | + &dev_attr_cabc_available_modes.attr, |
| 3434 | + NULL, |
| 3435 | +}; |
| 3436 | + |
| 3437 | +static struct attribute_group taal_attr_group = { |
| 3438 | + .attrs = taal_attrs, |
| 3439 | +}; |
| 3440 | + |
| 3441 | +static int taal_probe(struct omap_dss_device *dssdev) |
| 3442 | +{ |
| 3443 | + struct taal_data *td; |
| 3444 | + struct backlight_device *bldev; |
| 3445 | + int r; |
| 3446 | + |
| 3447 | + const struct omap_video_timings taal_panel_timings = { |
| 3448 | + .x_res = 864, |
| 3449 | + .y_res = 480, |
| 3450 | + }; |
| 3451 | + |
| 3452 | + dev_dbg(&dssdev->dev, "probe\n"); |
| 3453 | + |
| 3454 | + dssdev->panel.config = OMAP_DSS_LCD_TFT; |
| 3455 | + dssdev->panel.timings = taal_panel_timings; |
| 3456 | + dssdev->ctrl.pixel_size = 24; |
| 3457 | + |
| 3458 | + td = kzalloc(sizeof(*td), GFP_KERNEL); |
| 3459 | + if (!td) { |
| 3460 | + r = -ENOMEM; |
| 3461 | + goto err0; |
| 3462 | + } |
| 3463 | + |
| 3464 | + dev_set_drvdata(&dssdev->dev, td); |
| 3465 | + |
| 3466 | + dssdev->get_timings = taal_get_timings; |
| 3467 | + dssdev->get_resolution = taal_get_resolution; |
| 3468 | + |
| 3469 | + /* if no platform set_backlight() defined, presume DSI backlight |
| 3470 | + * control */ |
| 3471 | + if (!dssdev->set_backlight) |
| 3472 | + td->use_dsi_bl = true; |
| 3473 | + |
| 3474 | + bldev = backlight_device_register("taal", &dssdev->dev, dssdev, |
| 3475 | + &taal_bl_ops); |
| 3476 | + if (IS_ERR(bldev)) { |
| 3477 | + r = PTR_ERR(bldev); |
| 3478 | + goto err1; |
| 3479 | + } |
| 3480 | + |
| 3481 | + td->bldev = bldev; |
| 3482 | + |
| 3483 | + bldev->props.fb_blank = FB_BLANK_UNBLANK; |
| 3484 | + bldev->props.power = FB_BLANK_UNBLANK; |
| 3485 | + if (td->use_dsi_bl) { |
| 3486 | + bldev->props.max_brightness = 255; |
| 3487 | + bldev->props.brightness = 255; |
| 3488 | + } else { |
| 3489 | + bldev->props.max_brightness = 127; |
| 3490 | + bldev->props.brightness = 127; |
| 3491 | + } |
| 3492 | + |
| 3493 | + taal_bl_update_status(bldev); |
| 3494 | + |
| 3495 | + if (dssdev->phy.dsi.ext_te) { |
| 3496 | + int gpio = dssdev->phy.dsi.ext_te_gpio; |
| 3497 | + |
| 3498 | + r = gpio_request(gpio, "taal irq"); |
| 3499 | + if (r) { |
| 3500 | + dev_err(&dssdev->dev, "GPIO request failed\n"); |
| 3501 | + goto err2; |
| 3502 | + } |
| 3503 | + |
| 3504 | + gpio_direction_input(gpio); |
| 3505 | + |
| 3506 | + r = request_irq(gpio_to_irq(gpio), taal_te_isr, |
| 3507 | + IRQF_DISABLED | IRQF_TRIGGER_RISING, |
| 3508 | + "taal vsync", dssdev); |
| 3509 | + |
| 3510 | + if (r) { |
| 3511 | + dev_err(&dssdev->dev, "IRQ request failed\n"); |
| 3512 | + gpio_free(gpio); |
| 3513 | + goto err2; |
| 3514 | + } |
| 3515 | + |
| 3516 | + init_completion(&td->te_completion); |
| 3517 | + |
| 3518 | + td->use_ext_te = true; |
| 3519 | + } |
| 3520 | + |
| 3521 | + r = sysfs_create_group(&dssdev->dev.kobj, &taal_attr_group); |
| 3522 | + if (r) { |
| 3523 | + dev_err(&dssdev->dev, "failed to create sysfs files\n"); |
| 3524 | + goto err3; |
| 3525 | + } |
| 3526 | + |
| 3527 | + return 0; |
| 3528 | +err3: |
| 3529 | + if (td->use_ext_te) { |
| 3530 | + int gpio = dssdev->phy.dsi.ext_te_gpio; |
| 3531 | + free_irq(gpio_to_irq(gpio), dssdev); |
| 3532 | + gpio_free(gpio); |
| 3533 | + } |
| 3534 | +err2: |
| 3535 | + backlight_device_unregister(bldev); |
| 3536 | +err1: |
| 3537 | + kfree(td); |
| 3538 | +err0: |
| 3539 | + return r; |
| 3540 | +} |
| 3541 | + |
| 3542 | +static void taal_remove(struct omap_dss_device *dssdev) |
| 3543 | +{ |
| 3544 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3545 | + struct backlight_device *bldev; |
| 3546 | + |
| 3547 | + dev_dbg(&dssdev->dev, "remove\n"); |
| 3548 | + |
| 3549 | + sysfs_remove_group(&dssdev->dev.kobj, &taal_attr_group); |
| 3550 | + |
| 3551 | + if (td->use_ext_te) { |
| 3552 | + int gpio = dssdev->phy.dsi.ext_te_gpio; |
| 3553 | + free_irq(gpio_to_irq(gpio), dssdev); |
| 3554 | + gpio_free(gpio); |
| 3555 | + } |
| 3556 | + |
| 3557 | + bldev = td->bldev; |
| 3558 | + bldev->props.power = FB_BLANK_POWERDOWN; |
| 3559 | + taal_bl_update_status(bldev); |
| 3560 | + backlight_device_unregister(bldev); |
| 3561 | + |
| 3562 | + kfree(td); |
| 3563 | +} |
| 3564 | + |
| 3565 | +static int taal_enable(struct omap_dss_device *dssdev) |
| 3566 | +{ |
| 3567 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3568 | + u8 id1, id2, id3; |
| 3569 | + int r; |
| 3570 | + |
| 3571 | + dev_dbg(&dssdev->dev, "enable\n"); |
| 3572 | + |
| 3573 | + if (dssdev->platform_enable) { |
| 3574 | + r = dssdev->platform_enable(dssdev); |
| 3575 | + if (r) |
| 3576 | + return r; |
| 3577 | + } |
| 3578 | + |
| 3579 | + /* it seems we have to wait a bit until taal is ready */ |
| 3580 | + msleep(5); |
| 3581 | + |
| 3582 | + r = taal_sleep_out(td); |
| 3583 | + if (r) |
| 3584 | + return r; |
| 3585 | + |
| 3586 | + r = taal_get_id(&id1, &id2, &id3); |
| 3587 | + if (r) |
| 3588 | + return r; |
| 3589 | + |
| 3590 | + /* on early revisions CABC is broken */ |
| 3591 | + if (id2 == 0x00 || id2 == 0xff || id2 == 0x81) |
| 3592 | + td->cabc_broken = true; |
| 3593 | + |
| 3594 | + taal_dcs_write_1(DCS_BRIGHTNESS, 0xff); |
| 3595 | + taal_dcs_write_1(DCS_CTRL_DISPLAY, (1<<2) | (1<<5)); /* BL | BCTRL */ |
| 3596 | + |
| 3597 | + taal_dcs_write_1(DCS_PIXEL_FORMAT, 0x7); /* 24bit/pixel */ |
| 3598 | + |
| 3599 | + taal_set_addr_mode(td->rotate, td->mirror); |
| 3600 | + if (!td->cabc_broken) |
| 3601 | + taal_dcs_write_1(DCS_WRITE_CABC, td->cabc_mode); |
| 3602 | + |
| 3603 | + taal_dcs_write_0(DCS_DISPLAY_ON); |
| 3604 | + |
| 3605 | + td->enabled = 1; |
| 3606 | + |
| 3607 | + if (!td->intro_printed) { |
| 3608 | + dev_info(&dssdev->dev, "revision %02x.%02x.%02x\n", |
| 3609 | + id1, id2, id3); |
| 3610 | + if (td->cabc_broken) |
| 3611 | + dev_info(&dssdev->dev, |
| 3612 | + "old Taal version, CABC disabled\n"); |
| 3613 | + td->intro_printed = true; |
| 3614 | + } |
| 3615 | + |
| 3616 | + return 0; |
| 3617 | +} |
| 3618 | + |
| 3619 | +static void taal_disable(struct omap_dss_device *dssdev) |
| 3620 | +{ |
| 3621 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3622 | + |
| 3623 | + dev_dbg(&dssdev->dev, "disable\n"); |
| 3624 | + |
| 3625 | + taal_dcs_write_0(DCS_DISPLAY_OFF); |
| 3626 | + taal_sleep_in(td); |
| 3627 | + |
| 3628 | + /* wait a bit so that the message goes through */ |
| 3629 | + msleep(10); |
| 3630 | + |
| 3631 | + if (dssdev->platform_disable) |
| 3632 | + dssdev->platform_disable(dssdev); |
| 3633 | + |
| 3634 | + td->enabled = 0; |
| 3635 | +} |
| 3636 | + |
| 3637 | +static int taal_suspend(struct omap_dss_device *dssdev) |
| 3638 | +{ |
| 3639 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3640 | + struct backlight_device *bldev = td->bldev; |
| 3641 | + |
| 3642 | + bldev->props.power = FB_BLANK_POWERDOWN; |
| 3643 | + taal_bl_update_status(bldev); |
| 3644 | + |
| 3645 | + return 0; |
| 3646 | +} |
| 3647 | + |
| 3648 | +static int taal_resume(struct omap_dss_device *dssdev) |
| 3649 | +{ |
| 3650 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3651 | + struct backlight_device *bldev = td->bldev; |
| 3652 | + |
| 3653 | + bldev->props.power = FB_BLANK_UNBLANK; |
| 3654 | + taal_bl_update_status(bldev); |
| 3655 | + |
| 3656 | + return 0; |
| 3657 | +} |
| 3658 | + |
| 3659 | +static void taal_setup_update(struct omap_dss_device *dssdev, |
| 3660 | + u16 x, u16 y, u16 w, u16 h) |
| 3661 | +{ |
| 3662 | + taal_set_update_window(x, y, w, h); |
| 3663 | +} |
| 3664 | + |
| 3665 | +static int taal_enable_te(struct omap_dss_device *dssdev, bool enable) |
| 3666 | +{ |
| 3667 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3668 | + int r; |
| 3669 | + |
| 3670 | + td->te_enabled = enable; |
| 3671 | + |
| 3672 | + if (enable) |
| 3673 | + r = taal_dcs_write_1(DCS_TEAR_ON, 0); |
| 3674 | + else |
| 3675 | + r = taal_dcs_write_0(DCS_TEAR_OFF); |
| 3676 | + |
| 3677 | + return r; |
| 3678 | +} |
| 3679 | + |
| 3680 | +static int taal_wait_te(struct omap_dss_device *dssdev) |
| 3681 | +{ |
| 3682 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3683 | + long wait = msecs_to_jiffies(500); |
| 3684 | + |
| 3685 | + if (!td->use_ext_te || !td->te_enabled) |
| 3686 | + return 0; |
| 3687 | + |
| 3688 | + INIT_COMPLETION(td->te_completion); |
| 3689 | + wait = wait_for_completion_timeout(&td->te_completion, wait); |
| 3690 | + if (wait == 0) { |
| 3691 | + dev_err(&dssdev->dev, "timeout waiting TE\n"); |
| 3692 | + return -ETIME; |
| 3693 | + } |
| 3694 | + |
| 3695 | + return 0; |
| 3696 | +} |
| 3697 | + |
| 3698 | +static int taal_rotate(struct omap_dss_device *dssdev, u8 rotate) |
| 3699 | +{ |
| 3700 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3701 | + int r; |
| 3702 | + |
| 3703 | + dev_dbg(&dssdev->dev, "rotate %d\n", rotate); |
| 3704 | + |
| 3705 | + if (td->enabled) { |
| 3706 | + r = taal_set_addr_mode(rotate, td->mirror); |
| 3707 | + |
| 3708 | + if (r) |
| 3709 | + return r; |
| 3710 | + } |
| 3711 | + |
| 3712 | + td->rotate = rotate; |
| 3713 | + |
| 3714 | + return 0; |
| 3715 | +} |
| 3716 | + |
| 3717 | +static u8 taal_get_rotate(struct omap_dss_device *dssdev) |
| 3718 | +{ |
| 3719 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3720 | + return td->rotate; |
| 3721 | +} |
| 3722 | + |
| 3723 | +static int taal_mirror(struct omap_dss_device *dssdev, bool enable) |
| 3724 | +{ |
| 3725 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3726 | + int r; |
| 3727 | + |
| 3728 | + dev_dbg(&dssdev->dev, "mirror %d\n", enable); |
| 3729 | + |
| 3730 | + if (td->enabled) { |
| 3731 | + r = taal_set_addr_mode(td->rotate, enable); |
| 3732 | + |
| 3733 | + if (r) |
| 3734 | + return r; |
| 3735 | + } |
| 3736 | + |
| 3737 | + td->mirror = enable; |
| 3738 | + |
| 3739 | + return 0; |
| 3740 | +} |
| 3741 | + |
| 3742 | +static bool taal_get_mirror(struct omap_dss_device *dssdev) |
| 3743 | +{ |
| 3744 | + struct taal_data *td = dev_get_drvdata(&dssdev->dev); |
| 3745 | + return td->mirror; |
| 3746 | +} |
| 3747 | + |
| 3748 | +static int taal_run_test(struct omap_dss_device *dssdev, int test_num) |
| 3749 | +{ |
| 3750 | + u8 id1, id2, id3; |
| 3751 | + int r; |
| 3752 | + |
| 3753 | + r = taal_dcs_read_1(DCS_GET_ID1, &id1); |
| 3754 | + if (r) |
| 3755 | + return r; |
| 3756 | + r = taal_dcs_read_1(DCS_GET_ID2, &id2); |
| 3757 | + if (r) |
| 3758 | + return r; |
| 3759 | + r = taal_dcs_read_1(DCS_GET_ID3, &id3); |
| 3760 | + if (r) |
| 3761 | + return r; |
| 3762 | + |
| 3763 | + return 0; |
| 3764 | +} |
| 3765 | + |
| 3766 | +static int taal_memory_read(struct omap_dss_device *dssdev, |
| 3767 | + void *buf, size_t size, |
| 3768 | + u16 x, u16 y, u16 w, u16 h) |
| 3769 | +{ |
| 3770 | + int r; |
| 3771 | + int first = 1; |
| 3772 | + int plen; |
| 3773 | + unsigned buf_used = 0; |
| 3774 | + |
| 3775 | + if (size < w * h * 3) |
| 3776 | + return -ENOMEM; |
| 3777 | + |
| 3778 | + size = min(w * h * 3, |
| 3779 | + dssdev->panel.timings.x_res * |
| 3780 | + dssdev->panel.timings.y_res * 3); |
| 3781 | + |
| 3782 | + /* plen 1 or 2 goes into short packet. until checksum error is fixed, |
| 3783 | + * use short packets. plen 32 works, but bigger packets seem to cause |
| 3784 | + * an error. */ |
| 3785 | + if (size % 2) |
| 3786 | + plen = 1; |
| 3787 | + else |
| 3788 | + plen = 2; |
| 3789 | + |
| 3790 | + taal_setup_update(dssdev, x, y, w, h); |
| 3791 | + |
| 3792 | + r = dsi_vc_set_max_rx_packet_size(TCH, plen); |
| 3793 | + if (r) |
| 3794 | + return r; |
| 3795 | + |
| 3796 | + while (buf_used < size) { |
| 3797 | + u8 dcs_cmd = first ? 0x2e : 0x3e; |
| 3798 | + first = 0; |
| 3799 | + |
| 3800 | + r = dsi_vc_dcs_read(TCH, dcs_cmd, |
| 3801 | + buf + buf_used, size - buf_used); |
| 3802 | + |
| 3803 | + if (r < 0) { |
| 3804 | + dev_err(&dssdev->dev, "read error\n"); |
| 3805 | + goto err; |
| 3806 | + } |
| 3807 | + |
| 3808 | + buf_used += r; |
| 3809 | + |
| 3810 | + if (r < plen) { |
| 3811 | + dev_err(&dssdev->dev, "short read\n"); |
| 3812 | + break; |
| 3813 | + } |
| 3814 | + } |
| 3815 | + |
| 3816 | + r = buf_used; |
| 3817 | + |
| 3818 | +err: |
| 3819 | + dsi_vc_set_max_rx_packet_size(TCH, 1); |
| 3820 | + |
| 3821 | + return r; |
| 3822 | +} |
| 3823 | + |
| 3824 | +static struct omap_dss_driver taal_driver = { |
| 3825 | + .probe = taal_probe, |
| 3826 | + .remove = taal_remove, |
| 3827 | + |
| 3828 | + .enable = taal_enable, |
| 3829 | + .disable = taal_disable, |
| 3830 | + .suspend = taal_suspend, |
| 3831 | + .resume = taal_resume, |
| 3832 | + |
| 3833 | + .setup_update = taal_setup_update, |
| 3834 | + .enable_te = taal_enable_te, |
| 3835 | + .wait_for_te = taal_wait_te, |
| 3836 | + .set_rotate = taal_rotate, |
| 3837 | + .get_rotate = taal_get_rotate, |
| 3838 | + .set_mirror = taal_mirror, |
| 3839 | + .get_mirror = taal_get_mirror, |
| 3840 | + .run_test = taal_run_test, |
| 3841 | + .memory_read = taal_memory_read, |
| 3842 | + |
| 3843 | + .driver = { |
| 3844 | + .name = "taal", |
| 3845 | + .owner = THIS_MODULE, |
| 3846 | + }, |
| 3847 | +}; |
| 3848 | + |
| 3849 | +static int __init taal_init(void) |
| 3850 | +{ |
| 3851 | + omap_dss_register_driver(&taal_driver); |
| 3852 | + |
| 3853 | + return 0; |
| 3854 | +} |
| 3855 | + |
| 3856 | +static void __exit taal_exit(void) |
| 3857 | +{ |
| 3858 | + omap_dss_unregister_driver(&taal_driver); |
| 3859 | +} |
| 3860 | + |
| 3861 | +module_init(taal_init); |
| 3862 | +module_exit(taal_exit); |
| 3863 | + |
| 3864 | +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); |
| 3865 | +MODULE_DESCRIPTION("Taal Driver"); |
| 3866 | +MODULE_LICENSE("GPL"); |
| 3867 | --- /dev/null |
| 3868 | +++ b/drivers/video/omap2/dss/Kconfig |
| 3869 | @@ -0,0 +1,89 @@ |
| 3870 | +menuconfig OMAP2_DSS |
| 3871 | + tristate "OMAP2/3 Display Subsystem support (EXPERIMENTAL)" |
| 3872 | + depends on ARCH_OMAP2 || ARCH_OMAP3 |
| 3873 | + help |
| 3874 | + OMAP2/3 Display Subsystem support. |
| 3875 | + |
| 3876 | +if OMAP2_DSS |
| 3877 | + |
| 3878 | +config OMAP2_VRAM_SIZE |
| 3879 | + int "VRAM size (MB)" |
| 3880 | + range 0 32 |
| 3881 | + default 0 |
| 3882 | + help |
| 3883 | + The amount of SDRAM to reserve at boot time for video RAM use. |
| 3884 | + This VRAM will be used by omapfb and other drivers that need |
| 3885 | + large continuous RAM area for video use. |
| 3886 | + |
| 3887 | + You can also set this with "vram=<bytes>" kernel argument, or |
| 3888 | + in the board file. |
| 3889 | + |
| 3890 | +config OMAP2_DSS_DEBUG_SUPPORT |
| 3891 | + bool "Debug support" |
| 3892 | + default y |
| 3893 | + help |
| 3894 | + This enables debug messages. You need to enable printing |
| 3895 | + with 'debug' module parameter. |
| 3896 | + |
| 3897 | +config OMAP2_DSS_RFBI |
| 3898 | + bool "RFBI support" |
| 3899 | + default n |
| 3900 | + help |
| 3901 | + MIPI DBI, or RFBI (Remote Framebuffer Interface), support. |
| 3902 | + |
| 3903 | +config OMAP2_DSS_VENC |
| 3904 | + bool "VENC support" |
| 3905 | + default y |
| 3906 | + help |
| 3907 | + OMAP Video Encoder support. |
| 3908 | + |
| 3909 | +config OMAP2_DSS_SDI |
| 3910 | + bool "SDI support" |
| 3911 | + depends on ARCH_OMAP3 |
| 3912 | + default n |
| 3913 | + help |
| 3914 | + SDI (Serial Display Interface) support. |
| 3915 | + |
| 3916 | +config OMAP2_DSS_DSI |
| 3917 | + bool "DSI support" |
| 3918 | + depends on ARCH_OMAP3 |
| 3919 | + default n |
| 3920 | + help |
| 3921 | + MIPI DSI support. |
| 3922 | + |
| 3923 | +config OMAP2_DSS_USE_DSI_PLL |
| 3924 | + bool "Use DSI PLL for PCLK (EXPERIMENTAL)" |
| 3925 | + default n |
| 3926 | + depends on OMAP2_DSS_DSI |
| 3927 | + help |
| 3928 | + Use DSI PLL to generate pixel clock. Currently only for DPI output. |
| 3929 | + DSI PLL can be used to generate higher and more precise pixel clocks. |
| 3930 | + |
| 3931 | +config OMAP2_DSS_FAKE_VSYNC |
| 3932 | + bool "Fake VSYNC irq from manual update displays" |
| 3933 | + default n |
| 3934 | + help |
| 3935 | + If this is selected, DSI will generate a fake DISPC VSYNC interrupt |
| 3936 | + when DSI has sent a frame. This is only needed with DSI or RFBI |
| 3937 | + displays using manual mode, and you want VSYNC to, for example, |
| 3938 | + time animation. |
| 3939 | + |
| 3940 | +config OMAP2_DSS_MIN_FCK_PER_PCK |
| 3941 | + int "Minimum FCK/PCK ratio (for scaling)" |
| 3942 | + range 0 32 |
| 3943 | + default 0 |
| 3944 | + help |
| 3945 | + This can be used to adjust the minimum FCK/PCK ratio. |
| 3946 | + |
| 3947 | + With this you can make sure that DISPC FCK is at least |
| 3948 | + n x PCK. Video plane scaling requires higher FCK than |
| 3949 | + normally. |
| 3950 | + |
| 3951 | + If this is set to 0, there's no extra constraint on the |
| 3952 | + DISPC FCK. However, the FCK will at minimum be |
| 3953 | + 2xPCK (if active matrix) or 3xPCK (if passive matrix). |
| 3954 | + |
| 3955 | + Max FCK is 173MHz, so this doesn't work if your PCK |
| 3956 | + is very high. |
| 3957 | + |
| 3958 | +endif |
| 3959 | --- /dev/null |
| 3960 | +++ b/drivers/video/omap2/dss/Makefile |
| 3961 | @@ -0,0 +1,6 @@ |
| 3962 | +obj-$(CONFIG_OMAP2_DSS) += omapdss.o |
| 3963 | +omapdss-y := core.o dss.o dispc.o dpi.o display.o manager.o overlay.o |
| 3964 | +omapdss-$(CONFIG_OMAP2_DSS_RFBI) += rfbi.o |
| 3965 | +omapdss-$(CONFIG_OMAP2_DSS_VENC) += venc.o |
| 3966 | +omapdss-$(CONFIG_OMAP2_DSS_SDI) += sdi.o |
| 3967 | +omapdss-$(CONFIG_OMAP2_DSS_DSI) += dsi.o |
| 3968 | --- /dev/null |
| 3969 | +++ b/drivers/video/omap2/dss/core.c |
| 3970 | @@ -0,0 +1,917 @@ |
| 3971 | +/* |
| 3972 | + * linux/drivers/video/omap2/dss/core.c |
| 3973 | + * |
| 3974 | + * Copyright (C) 2009 Nokia Corporation |
| 3975 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 3976 | + * |
| 3977 | + * Some code and ideas taken from drivers/video/omap/ driver |
| 3978 | + * by Imre Deak. |
| 3979 | + * |
| 3980 | + * This program is free software; you can redistribute it and/or modify it |
| 3981 | + * under the terms of the GNU General Public License version 2 as published by |
| 3982 | + * the Free Software Foundation. |
| 3983 | + * |
| 3984 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 3985 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 3986 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 3987 | + * more details. |
| 3988 | + * |
| 3989 | + * You should have received a copy of the GNU General Public License along with |
| 3990 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 3991 | + */ |
| 3992 | + |
| 3993 | +#define DSS_SUBSYS_NAME "CORE" |
| 3994 | + |
| 3995 | +#include <linux/kernel.h> |
| 3996 | +#include <linux/module.h> |
| 3997 | +#include <linux/clk.h> |
| 3998 | +#include <linux/err.h> |
| 3999 | +#include <linux/platform_device.h> |
| 4000 | +#include <linux/seq_file.h> |
| 4001 | +#include <linux/debugfs.h> |
| 4002 | +#include <linux/io.h> |
| 4003 | +#include <linux/device.h> |
| 4004 | + |
| 4005 | +#include <mach/display.h> |
| 4006 | +#include <mach/clock.h> |
| 4007 | + |
| 4008 | +#include "dss.h" |
| 4009 | + |
| 4010 | +static struct { |
| 4011 | + struct platform_device *pdev; |
| 4012 | + int ctx_id; |
| 4013 | + |
| 4014 | + struct clk *dss_ick; |
| 4015 | + struct clk *dss1_fck; |
| 4016 | + struct clk *dss2_fck; |
| 4017 | + struct clk *dss_54m_fck; |
| 4018 | + struct clk *dss_96m_fck; |
| 4019 | + unsigned num_clks_enabled; |
| 4020 | +} core; |
| 4021 | + |
| 4022 | +static void dss_clk_enable_all_no_ctx(void); |
| 4023 | +static void dss_clk_disable_all_no_ctx(void); |
| 4024 | +static void dss_clk_enable_no_ctx(enum dss_clock clks); |
| 4025 | +static void dss_clk_disable_no_ctx(enum dss_clock clks); |
| 4026 | + |
| 4027 | +static char *def_disp_name; |
| 4028 | +module_param_named(def_disp, def_disp_name, charp, 0); |
| 4029 | +MODULE_PARM_DESC(def_disp_name, "default display name"); |
| 4030 | + |
| 4031 | +#ifdef DEBUG |
| 4032 | +unsigned int dss_debug; |
| 4033 | +module_param_named(debug, dss_debug, bool, 0644); |
| 4034 | +#endif |
| 4035 | + |
| 4036 | +/* CONTEXT */ |
| 4037 | +static int dss_get_ctx_id(void) |
| 4038 | +{ |
| 4039 | + struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; |
| 4040 | + int r; |
| 4041 | + |
| 4042 | + if (!pdata->get_last_off_on_transaction_id) |
| 4043 | + return 0; |
| 4044 | + r = pdata->get_last_off_on_transaction_id(&core.pdev->dev); |
| 4045 | + if (r < 0) { |
| 4046 | + dev_err(&core.pdev->dev, "getting transaction ID failed, " |
| 4047 | + "will force context restore\n"); |
| 4048 | + r = -1; |
| 4049 | + } |
| 4050 | + return r; |
| 4051 | +} |
| 4052 | + |
| 4053 | +int dss_need_ctx_restore(void) |
| 4054 | +{ |
| 4055 | + int id = dss_get_ctx_id(); |
| 4056 | + |
| 4057 | + if (id < 0 || id != core.ctx_id) { |
| 4058 | + DSSDBG("ctx id %d -> id %d\n", |
| 4059 | + core.ctx_id, id); |
| 4060 | + core.ctx_id = id; |
| 4061 | + return 1; |
| 4062 | + } else { |
| 4063 | + return 0; |
| 4064 | + } |
| 4065 | +} |
| 4066 | + |
| 4067 | +static void save_all_ctx(void) |
| 4068 | +{ |
| 4069 | + DSSDBG("save context\n"); |
| 4070 | + |
| 4071 | + dss_clk_enable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 4072 | + |
| 4073 | + dss_save_context(); |
| 4074 | + dispc_save_context(); |
| 4075 | +#ifdef CONFIG_OMAP2_DSS_DSI |
| 4076 | + dsi_save_context(); |
| 4077 | +#endif |
| 4078 | + |
| 4079 | + dss_clk_disable_no_ctx(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 4080 | +} |
| 4081 | + |
| 4082 | +static void restore_all_ctx(void) |
| 4083 | +{ |
| 4084 | + DSSDBG("restore context\n"); |
| 4085 | + |
| 4086 | + dss_clk_enable_all_no_ctx(); |
| 4087 | + |
| 4088 | + dss_restore_context(); |
| 4089 | + dispc_restore_context(); |
| 4090 | +#ifdef CONFIG_OMAP2_DSS_DSI |
| 4091 | + dsi_restore_context(); |
| 4092 | +#endif |
| 4093 | + |
| 4094 | + dss_clk_disable_all_no_ctx(); |
| 4095 | +} |
| 4096 | + |
| 4097 | +/* CLOCKS */ |
| 4098 | +void dss_dump_clocks(struct seq_file *s) |
| 4099 | +{ |
| 4100 | + int i; |
| 4101 | + struct clk *clocks[5] = { |
| 4102 | + core.dss_ick, |
| 4103 | + core.dss1_fck, |
| 4104 | + core.dss2_fck, |
| 4105 | + core.dss_54m_fck, |
| 4106 | + core.dss_96m_fck |
| 4107 | + }; |
| 4108 | + |
| 4109 | + seq_printf(s, "- dss -\n"); |
| 4110 | + |
| 4111 | + seq_printf(s, "internal clk count\t%u\n", core.num_clks_enabled); |
| 4112 | + |
| 4113 | + for (i = 0; i < 5; i++) { |
| 4114 | + if (!clocks[i]) |
| 4115 | + continue; |
| 4116 | + seq_printf(s, "%-15s\t%lu\t%d\n", |
| 4117 | + clocks[i]->name, |
| 4118 | + clk_get_rate(clocks[i]), |
| 4119 | + clocks[i]->usecount); |
| 4120 | + } |
| 4121 | +} |
| 4122 | + |
| 4123 | +static int dss_get_clock(struct clk **clock, const char *clk_name) |
| 4124 | +{ |
| 4125 | + struct clk *clk; |
| 4126 | + |
| 4127 | + clk = clk_get(&core.pdev->dev, clk_name); |
| 4128 | + |
| 4129 | + if (IS_ERR(clk)) { |
| 4130 | + DSSERR("can't get clock %s", clk_name); |
| 4131 | + return PTR_ERR(clk); |
| 4132 | + } |
| 4133 | + |
| 4134 | + *clock = clk; |
| 4135 | + |
| 4136 | + DSSDBG("clk %s, rate %ld\n", clk_name, clk_get_rate(clk)); |
| 4137 | + |
| 4138 | + return 0; |
| 4139 | +} |
| 4140 | + |
| 4141 | +static int dss_get_clocks(void) |
| 4142 | +{ |
| 4143 | + int r; |
| 4144 | + |
| 4145 | + core.dss_ick = NULL; |
| 4146 | + core.dss1_fck = NULL; |
| 4147 | + core.dss2_fck = NULL; |
| 4148 | + core.dss_54m_fck = NULL; |
| 4149 | + core.dss_96m_fck = NULL; |
| 4150 | + |
| 4151 | + r = dss_get_clock(&core.dss_ick, "ick"); |
| 4152 | + if (r) |
| 4153 | + goto err; |
| 4154 | + |
| 4155 | + r = dss_get_clock(&core.dss1_fck, "dss1_fck"); |
| 4156 | + if (r) |
| 4157 | + goto err; |
| 4158 | + |
| 4159 | + r = dss_get_clock(&core.dss2_fck, "dss2_fck"); |
| 4160 | + if (r) |
| 4161 | + goto err; |
| 4162 | + |
| 4163 | + r = dss_get_clock(&core.dss_54m_fck, "tv_fck"); |
| 4164 | + if (r) |
| 4165 | + goto err; |
| 4166 | + |
| 4167 | + r = dss_get_clock(&core.dss_96m_fck, "video_fck"); |
| 4168 | + if (r) |
| 4169 | + goto err; |
| 4170 | + |
| 4171 | + return 0; |
| 4172 | + |
| 4173 | +err: |
| 4174 | + if (core.dss_ick) |
| 4175 | + clk_put(core.dss_ick); |
| 4176 | + if (core.dss1_fck) |
| 4177 | + clk_put(core.dss1_fck); |
| 4178 | + if (core.dss2_fck) |
| 4179 | + clk_put(core.dss2_fck); |
| 4180 | + if (core.dss_54m_fck) |
| 4181 | + clk_put(core.dss_54m_fck); |
| 4182 | + if (core.dss_96m_fck) |
| 4183 | + clk_put(core.dss_96m_fck); |
| 4184 | + |
| 4185 | + return r; |
| 4186 | +} |
| 4187 | + |
| 4188 | +static void dss_put_clocks(void) |
| 4189 | +{ |
| 4190 | + if (core.dss_96m_fck) |
| 4191 | + clk_put(core.dss_96m_fck); |
| 4192 | + clk_put(core.dss_54m_fck); |
| 4193 | + clk_put(core.dss1_fck); |
| 4194 | + clk_put(core.dss2_fck); |
| 4195 | + clk_put(core.dss_ick); |
| 4196 | +} |
| 4197 | + |
| 4198 | +unsigned long dss_clk_get_rate(enum dss_clock clk) |
| 4199 | +{ |
| 4200 | + switch (clk) { |
| 4201 | + case DSS_CLK_ICK: |
| 4202 | + return clk_get_rate(core.dss_ick); |
| 4203 | + case DSS_CLK_FCK1: |
| 4204 | + return clk_get_rate(core.dss1_fck); |
| 4205 | + case DSS_CLK_FCK2: |
| 4206 | + return clk_get_rate(core.dss2_fck); |
| 4207 | + case DSS_CLK_54M: |
| 4208 | + return clk_get_rate(core.dss_54m_fck); |
| 4209 | + case DSS_CLK_96M: |
| 4210 | + return clk_get_rate(core.dss_96m_fck); |
| 4211 | + } |
| 4212 | + |
| 4213 | + BUG(); |
| 4214 | + return 0; |
| 4215 | +} |
| 4216 | + |
| 4217 | +static unsigned count_clk_bits(enum dss_clock clks) |
| 4218 | +{ |
| 4219 | + unsigned num_clks = 0; |
| 4220 | + |
| 4221 | + if (clks & DSS_CLK_ICK) |
| 4222 | + ++num_clks; |
| 4223 | + if (clks & DSS_CLK_FCK1) |
| 4224 | + ++num_clks; |
| 4225 | + if (clks & DSS_CLK_FCK2) |
| 4226 | + ++num_clks; |
| 4227 | + if (clks & DSS_CLK_54M) |
| 4228 | + ++num_clks; |
| 4229 | + if (clks & DSS_CLK_96M) |
| 4230 | + ++num_clks; |
| 4231 | + |
| 4232 | + return num_clks; |
| 4233 | +} |
| 4234 | + |
| 4235 | +static void dss_clk_enable_no_ctx(enum dss_clock clks) |
| 4236 | +{ |
| 4237 | + unsigned num_clks = count_clk_bits(clks); |
| 4238 | + |
| 4239 | + if (clks & DSS_CLK_ICK) |
| 4240 | + clk_enable(core.dss_ick); |
| 4241 | + if (clks & DSS_CLK_FCK1) |
| 4242 | + clk_enable(core.dss1_fck); |
| 4243 | + if (clks & DSS_CLK_FCK2) |
| 4244 | + clk_enable(core.dss2_fck); |
| 4245 | + if (clks & DSS_CLK_54M) |
| 4246 | + clk_enable(core.dss_54m_fck); |
| 4247 | + if (clks & DSS_CLK_96M) |
| 4248 | + clk_enable(core.dss_96m_fck); |
| 4249 | + |
| 4250 | + core.num_clks_enabled += num_clks; |
| 4251 | +} |
| 4252 | + |
| 4253 | +void dss_clk_enable(enum dss_clock clks) |
| 4254 | +{ |
| 4255 | + dss_clk_enable_no_ctx(clks); |
| 4256 | + |
| 4257 | + if (cpu_is_omap34xx() && dss_need_ctx_restore()) |
| 4258 | + restore_all_ctx(); |
| 4259 | +} |
| 4260 | + |
| 4261 | +static void dss_clk_disable_no_ctx(enum dss_clock clks) |
| 4262 | +{ |
| 4263 | + unsigned num_clks = count_clk_bits(clks); |
| 4264 | + |
| 4265 | + if (clks & DSS_CLK_ICK) |
| 4266 | + clk_disable(core.dss_ick); |
| 4267 | + if (clks & DSS_CLK_FCK1) |
| 4268 | + clk_disable(core.dss1_fck); |
| 4269 | + if (clks & DSS_CLK_FCK2) |
| 4270 | + clk_disable(core.dss2_fck); |
| 4271 | + if (clks & DSS_CLK_54M) |
| 4272 | + clk_disable(core.dss_54m_fck); |
| 4273 | + if (clks & DSS_CLK_96M) |
| 4274 | + clk_disable(core.dss_96m_fck); |
| 4275 | + |
| 4276 | + core.num_clks_enabled -= num_clks; |
| 4277 | +} |
| 4278 | + |
| 4279 | +void dss_clk_disable(enum dss_clock clks) |
| 4280 | +{ |
| 4281 | + if (cpu_is_omap34xx()) { |
| 4282 | + unsigned num_clks = count_clk_bits(clks); |
| 4283 | + |
| 4284 | + BUG_ON(core.num_clks_enabled < num_clks); |
| 4285 | + |
| 4286 | + if (core.num_clks_enabled == num_clks) |
| 4287 | + save_all_ctx(); |
| 4288 | + } |
| 4289 | + |
| 4290 | + dss_clk_disable_no_ctx(clks); |
| 4291 | +} |
| 4292 | + |
| 4293 | +static void dss_clk_enable_all_no_ctx(void) |
| 4294 | +{ |
| 4295 | + enum dss_clock clks; |
| 4296 | + |
| 4297 | + clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M; |
| 4298 | + if (cpu_is_omap34xx()) |
| 4299 | + clks |= DSS_CLK_96M; |
| 4300 | + dss_clk_enable_no_ctx(clks); |
| 4301 | +} |
| 4302 | + |
| 4303 | +static void dss_clk_disable_all_no_ctx(void) |
| 4304 | +{ |
| 4305 | + enum dss_clock clks; |
| 4306 | + |
| 4307 | + clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M; |
| 4308 | + if (cpu_is_omap34xx()) |
| 4309 | + clks |= DSS_CLK_96M; |
| 4310 | + dss_clk_disable_no_ctx(clks); |
| 4311 | +} |
| 4312 | + |
| 4313 | +static void dss_clk_disable_all(void) |
| 4314 | +{ |
| 4315 | + enum dss_clock clks; |
| 4316 | + |
| 4317 | + clks = DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_FCK2 | DSS_CLK_54M; |
| 4318 | + if (cpu_is_omap34xx()) |
| 4319 | + clks |= DSS_CLK_96M; |
| 4320 | + dss_clk_disable(clks); |
| 4321 | +} |
| 4322 | + |
| 4323 | +/* DEBUGFS */ |
| 4324 | +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) |
| 4325 | +static void dss_debug_dump_clocks(struct seq_file *s) |
| 4326 | +{ |
| 4327 | + dss_dump_clocks(s); |
| 4328 | + dispc_dump_clocks(s); |
| 4329 | +#ifdef CONFIG_OMAP2_DSS_DSI |
| 4330 | + dsi_dump_clocks(s); |
| 4331 | +#endif |
| 4332 | +} |
| 4333 | + |
| 4334 | +static int dss_debug_show(struct seq_file *s, void *unused) |
| 4335 | +{ |
| 4336 | + void (*func)(struct seq_file *) = s->private; |
| 4337 | + func(s); |
| 4338 | + return 0; |
| 4339 | +} |
| 4340 | + |
| 4341 | +static int dss_debug_open(struct inode *inode, struct file *file) |
| 4342 | +{ |
| 4343 | + return single_open(file, dss_debug_show, inode->i_private); |
| 4344 | +} |
| 4345 | + |
| 4346 | +static const struct file_operations dss_debug_fops = { |
| 4347 | + .open = dss_debug_open, |
| 4348 | + .read = seq_read, |
| 4349 | + .llseek = seq_lseek, |
| 4350 | + .release = single_release, |
| 4351 | +}; |
| 4352 | + |
| 4353 | +static struct dentry *dss_debugfs_dir; |
| 4354 | + |
| 4355 | +static int dss_initialize_debugfs(void) |
| 4356 | +{ |
| 4357 | + dss_debugfs_dir = debugfs_create_dir("omapdss", NULL); |
| 4358 | + if (IS_ERR(dss_debugfs_dir)) { |
| 4359 | + int err = PTR_ERR(dss_debugfs_dir); |
| 4360 | + dss_debugfs_dir = NULL; |
| 4361 | + return err; |
| 4362 | + } |
| 4363 | + |
| 4364 | + debugfs_create_file("clk", S_IRUGO, dss_debugfs_dir, |
| 4365 | + &dss_debug_dump_clocks, &dss_debug_fops); |
| 4366 | + |
| 4367 | + debugfs_create_file("dss", S_IRUGO, dss_debugfs_dir, |
| 4368 | + &dss_dump_regs, &dss_debug_fops); |
| 4369 | + debugfs_create_file("dispc", S_IRUGO, dss_debugfs_dir, |
| 4370 | + &dispc_dump_regs, &dss_debug_fops); |
| 4371 | +#ifdef CONFIG_OMAP2_DSS_RFBI |
| 4372 | + debugfs_create_file("rfbi", S_IRUGO, dss_debugfs_dir, |
| 4373 | + &rfbi_dump_regs, &dss_debug_fops); |
| 4374 | +#endif |
| 4375 | +#ifdef CONFIG_OMAP2_DSS_DSI |
| 4376 | + debugfs_create_file("dsi", S_IRUGO, dss_debugfs_dir, |
| 4377 | + &dsi_dump_regs, &dss_debug_fops); |
| 4378 | +#endif |
| 4379 | +#ifdef CONFIG_OMAP2_DSS_VENC |
| 4380 | + debugfs_create_file("venc", S_IRUGO, dss_debugfs_dir, |
| 4381 | + &venc_dump_regs, &dss_debug_fops); |
| 4382 | +#endif |
| 4383 | + return 0; |
| 4384 | +} |
| 4385 | + |
| 4386 | +static void dss_uninitialize_debugfs(void) |
| 4387 | +{ |
| 4388 | + if (dss_debugfs_dir) |
| 4389 | + debugfs_remove_recursive(dss_debugfs_dir); |
| 4390 | +} |
| 4391 | +#endif /* CONFIG_DEBUG_FS && CONFIG_OMAP2_DSS_DEBUG_SUPPORT */ |
| 4392 | + |
| 4393 | +/* PLATFORM DEVICE */ |
| 4394 | +static int omap_dss_probe(struct platform_device *pdev) |
| 4395 | +{ |
| 4396 | + struct omap_dss_board_info *pdata = pdev->dev.platform_data; |
| 4397 | + int skip_init = 0; |
| 4398 | + int r; |
| 4399 | + int i; |
| 4400 | + |
| 4401 | + core.pdev = pdev; |
| 4402 | + |
| 4403 | + dss_init_overlay_managers(pdev); |
| 4404 | + dss_init_overlays(pdev); |
| 4405 | + |
| 4406 | + r = dss_get_clocks(); |
| 4407 | + if (r) |
| 4408 | + goto fail0; |
| 4409 | + |
| 4410 | + dss_clk_enable_all_no_ctx(); |
| 4411 | + |
| 4412 | + core.ctx_id = dss_get_ctx_id(); |
| 4413 | + DSSDBG("initial ctx id %u\n", core.ctx_id); |
| 4414 | + |
| 4415 | +#ifdef CONFIG_FB_OMAP_BOOTLOADER_INIT |
| 4416 | + /* DISPC_CONTROL */ |
| 4417 | + if (omap_readl(0x48050440) & 1) /* LCD enabled? */ |
| 4418 | + skip_init = 1; |
| 4419 | +#endif |
| 4420 | + |
| 4421 | + r = dss_init(skip_init); |
| 4422 | + if (r) { |
| 4423 | + DSSERR("Failed to initialize DSS\n"); |
| 4424 | + goto fail0; |
| 4425 | + } |
| 4426 | + |
| 4427 | +#ifdef CONFIG_OMAP2_DSS_RFBI |
| 4428 | + r = rfbi_init(); |
| 4429 | + if (r) { |
| 4430 | + DSSERR("Failed to initialize rfbi\n"); |
| 4431 | + goto fail0; |
| 4432 | + } |
| 4433 | +#endif |
| 4434 | + |
| 4435 | + r = dpi_init(); |
| 4436 | + if (r) { |
| 4437 | + DSSERR("Failed to initialize dpi\n"); |
| 4438 | + goto fail0; |
| 4439 | + } |
| 4440 | + |
| 4441 | + r = dispc_init(); |
| 4442 | + if (r) { |
| 4443 | + DSSERR("Failed to initialize dispc\n"); |
| 4444 | + goto fail0; |
| 4445 | + } |
| 4446 | +#ifdef CONFIG_OMAP2_DSS_VENC |
| 4447 | + r = venc_init(pdev); |
| 4448 | + if (r) { |
| 4449 | + DSSERR("Failed to initialize venc\n"); |
| 4450 | + goto fail0; |
| 4451 | + } |
| 4452 | +#endif |
| 4453 | + if (cpu_is_omap34xx()) { |
| 4454 | +#ifdef CONFIG_OMAP2_DSS_SDI |
| 4455 | + r = sdi_init(skip_init); |
| 4456 | + if (r) { |
| 4457 | + DSSERR("Failed to initialize SDI\n"); |
| 4458 | + goto fail0; |
| 4459 | + } |
| 4460 | +#endif |
| 4461 | +#ifdef CONFIG_OMAP2_DSS_DSI |
| 4462 | + r = dsi_init(pdev); |
| 4463 | + if (r) { |
| 4464 | + DSSERR("Failed to initialize DSI\n"); |
| 4465 | + goto fail0; |
| 4466 | + } |
| 4467 | +#endif |
| 4468 | + } |
| 4469 | + |
| 4470 | +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) |
| 4471 | + r = dss_initialize_debugfs(); |
| 4472 | + if (r) |
| 4473 | + goto fail0; |
| 4474 | +#endif |
| 4475 | + |
| 4476 | + for (i = 0; i < pdata->num_devices; ++i) { |
| 4477 | + struct omap_dss_device *dssdev = pdata->devices[i]; |
| 4478 | + |
| 4479 | + r = omap_dss_register_device(dssdev); |
| 4480 | + if (r) |
| 4481 | + DSSERR("device reg failed %d\n", i); |
| 4482 | + |
| 4483 | + if (def_disp_name && strcmp(def_disp_name, dssdev->name) == 0) |
| 4484 | + pdata->default_device = dssdev; |
| 4485 | + } |
| 4486 | + |
| 4487 | + dss_clk_disable_all(); |
| 4488 | + |
| 4489 | + return 0; |
| 4490 | + |
| 4491 | + /* XXX fail correctly */ |
| 4492 | +fail0: |
| 4493 | + return r; |
| 4494 | +} |
| 4495 | + |
| 4496 | +static int omap_dss_remove(struct platform_device *pdev) |
| 4497 | +{ |
| 4498 | + struct omap_dss_board_info *pdata = pdev->dev.platform_data; |
| 4499 | + int i; |
| 4500 | + int c; |
| 4501 | + |
| 4502 | +#if defined(CONFIG_DEBUG_FS) && defined(CONFIG_OMAP2_DSS_DEBUG_SUPPORT) |
| 4503 | + dss_uninitialize_debugfs(); |
| 4504 | +#endif |
| 4505 | + |
| 4506 | +#ifdef CONFIG_OMAP2_DSS_VENC |
| 4507 | + venc_exit(); |
| 4508 | +#endif |
| 4509 | + dispc_exit(); |
| 4510 | + dpi_exit(); |
| 4511 | +#ifdef CONFIG_OMAP2_DSS_RFBI |
| 4512 | + rfbi_exit(); |
| 4513 | +#endif |
| 4514 | + if (cpu_is_omap34xx()) { |
| 4515 | +#ifdef CONFIG_OMAP2_DSS_DSI |
| 4516 | + dsi_exit(); |
| 4517 | +#endif |
| 4518 | +#ifdef CONFIG_OMAP2_DSS_SDI |
| 4519 | + sdi_exit(); |
| 4520 | +#endif |
| 4521 | + } |
| 4522 | + |
| 4523 | + dss_exit(); |
| 4524 | + |
| 4525 | + /* these should be removed at some point */ |
| 4526 | + c = core.dss_ick->usecount; |
| 4527 | + if (c > 0) { |
| 4528 | + DSSERR("warning: dss_ick usecount %d, disabling\n", c); |
| 4529 | + while (c-- > 0) |
| 4530 | + clk_disable(core.dss_ick); |
| 4531 | + } |
| 4532 | + |
| 4533 | + c = core.dss1_fck->usecount; |
| 4534 | + if (c > 0) { |
| 4535 | + DSSERR("warning: dss1_fck usecount %d, disabling\n", c); |
| 4536 | + while (c-- > 0) |
| 4537 | + clk_disable(core.dss1_fck); |
| 4538 | + } |
| 4539 | + |
| 4540 | + c = core.dss2_fck->usecount; |
| 4541 | + if (c > 0) { |
| 4542 | + DSSERR("warning: dss2_fck usecount %d, disabling\n", c); |
| 4543 | + while (c-- > 0) |
| 4544 | + clk_disable(core.dss2_fck); |
| 4545 | + } |
| 4546 | + |
| 4547 | + c = core.dss_54m_fck->usecount; |
| 4548 | + if (c > 0) { |
| 4549 | + DSSERR("warning: dss_54m_fck usecount %d, disabling\n", c); |
| 4550 | + while (c-- > 0) |
| 4551 | + clk_disable(core.dss_54m_fck); |
| 4552 | + } |
| 4553 | + |
| 4554 | + if (core.dss_96m_fck) { |
| 4555 | + c = core.dss_96m_fck->usecount; |
| 4556 | + if (c > 0) { |
| 4557 | + DSSERR("warning: dss_96m_fck usecount %d, disabling\n", |
| 4558 | + c); |
| 4559 | + while (c-- > 0) |
| 4560 | + clk_disable(core.dss_96m_fck); |
| 4561 | + } |
| 4562 | + } |
| 4563 | + |
| 4564 | + dss_put_clocks(); |
| 4565 | + |
| 4566 | + dss_uninit_overlays(pdev); |
| 4567 | + dss_uninit_overlay_managers(pdev); |
| 4568 | + |
| 4569 | + for (i = 0; i < pdata->num_devices; ++i) |
| 4570 | + omap_dss_unregister_device(pdata->devices[i]); |
| 4571 | + |
| 4572 | + return 0; |
| 4573 | +} |
| 4574 | + |
| 4575 | +static void omap_dss_shutdown(struct platform_device *pdev) |
| 4576 | +{ |
| 4577 | + DSSDBG("shutdown\n"); |
| 4578 | +} |
| 4579 | + |
| 4580 | +static int omap_dss_suspend(struct platform_device *pdev, pm_message_t state) |
| 4581 | +{ |
| 4582 | + DSSDBG("suspend %d\n", state.event); |
| 4583 | + |
| 4584 | + return dss_suspend_all_devices(); |
| 4585 | +} |
| 4586 | + |
| 4587 | +static int omap_dss_resume(struct platform_device *pdev) |
| 4588 | +{ |
| 4589 | + DSSDBG("resume\n"); |
| 4590 | + |
| 4591 | + return dss_resume_all_devices(); |
| 4592 | +} |
| 4593 | + |
| 4594 | +static struct platform_driver omap_dss_driver = { |
| 4595 | + .probe = omap_dss_probe, |
| 4596 | + .remove = omap_dss_remove, |
| 4597 | + .shutdown = omap_dss_shutdown, |
| 4598 | + .suspend = omap_dss_suspend, |
| 4599 | + .resume = omap_dss_resume, |
| 4600 | + .driver = { |
| 4601 | + .name = "omapdss", |
| 4602 | + .owner = THIS_MODULE, |
| 4603 | + }, |
| 4604 | +}; |
| 4605 | + |
| 4606 | +/* BUS */ |
| 4607 | +static int dss_bus_match(struct device *dev, struct device_driver *driver) |
| 4608 | +{ |
| 4609 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 4610 | + |
| 4611 | + DSSDBG("bus_match. dev %s/%s, drv %s\n", |
| 4612 | + dev_name(dev), dssdev->driver_name, driver->name); |
| 4613 | + |
| 4614 | + return strcmp(dssdev->driver_name, driver->name) == 0; |
| 4615 | +} |
| 4616 | + |
| 4617 | +static ssize_t device_name_show(struct device *dev, |
| 4618 | + struct device_attribute *attr, char *buf) |
| 4619 | +{ |
| 4620 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 4621 | + return snprintf(buf, PAGE_SIZE, "%s\n", |
| 4622 | + dssdev->name ? |
| 4623 | + dssdev->name : ""); |
| 4624 | +} |
| 4625 | + |
| 4626 | +static struct device_attribute default_dev_attrs[] = { |
| 4627 | + __ATTR(name, S_IRUGO, device_name_show, NULL), |
| 4628 | + __ATTR_NULL, |
| 4629 | +}; |
| 4630 | + |
| 4631 | +static ssize_t driver_name_show(struct device_driver *drv, char *buf) |
| 4632 | +{ |
| 4633 | + struct omap_dss_driver *dssdrv = to_dss_driver(drv); |
| 4634 | + return snprintf(buf, PAGE_SIZE, "%s\n", |
| 4635 | + dssdrv->driver.name ? |
| 4636 | + dssdrv->driver.name : ""); |
| 4637 | +} |
| 4638 | +static struct driver_attribute default_drv_attrs[] = { |
| 4639 | + __ATTR(name, S_IRUGO, driver_name_show, NULL), |
| 4640 | + __ATTR_NULL, |
| 4641 | +}; |
| 4642 | + |
| 4643 | +static struct bus_type dss_bus_type = { |
| 4644 | + .name = "omapdss", |
| 4645 | + .match = dss_bus_match, |
| 4646 | + .dev_attrs = default_dev_attrs, |
| 4647 | + .drv_attrs = default_drv_attrs, |
| 4648 | +}; |
| 4649 | + |
| 4650 | +static void dss_bus_release(struct device *dev) |
| 4651 | +{ |
| 4652 | + DSSDBG("bus_release\n"); |
| 4653 | +} |
| 4654 | + |
| 4655 | +static struct device dss_bus = { |
| 4656 | + .release = dss_bus_release, |
| 4657 | +}; |
| 4658 | + |
| 4659 | +struct bus_type *dss_get_bus(void) |
| 4660 | +{ |
| 4661 | + return &dss_bus_type; |
| 4662 | +} |
| 4663 | + |
| 4664 | +/* DRIVER */ |
| 4665 | +static int dss_driver_probe(struct device *dev) |
| 4666 | +{ |
| 4667 | + int r; |
| 4668 | + struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver); |
| 4669 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 4670 | + struct omap_dss_board_info *pdata = core.pdev->dev.platform_data; |
| 4671 | + bool force; |
| 4672 | + |
| 4673 | + DSSDBG("driver_probe: dev %s/%s, drv %s\n", |
| 4674 | + dev_name(dev), dssdev->driver_name, |
| 4675 | + dssdrv->driver.name); |
| 4676 | + |
| 4677 | + dss_init_device(core.pdev, dssdev); |
| 4678 | + |
| 4679 | + /* skip this if the device is behind a ctrl */ |
| 4680 | + if (!dssdev->panel.ctrl) { |
| 4681 | + force = pdata->default_device == dssdev; |
| 4682 | + dss_recheck_connections(dssdev, force); |
| 4683 | + } |
| 4684 | + |
| 4685 | + r = dssdrv->probe(dssdev); |
| 4686 | + |
| 4687 | + if (r) { |
| 4688 | + DSSERR("driver probe failed: %d\n", r); |
| 4689 | + return r; |
| 4690 | + } |
| 4691 | + |
| 4692 | + DSSDBG("probe done for device %s\n", dev_name(dev)); |
| 4693 | + |
| 4694 | + dssdev->driver = dssdrv; |
| 4695 | + |
| 4696 | + return 0; |
| 4697 | +} |
| 4698 | + |
| 4699 | +static int dss_driver_remove(struct device *dev) |
| 4700 | +{ |
| 4701 | + struct omap_dss_driver *dssdrv = to_dss_driver(dev->driver); |
| 4702 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 4703 | + |
| 4704 | + DSSDBG("driver_remove: dev %s/%s\n", dev_name(dev), |
| 4705 | + dssdev->driver_name); |
| 4706 | + |
| 4707 | + dssdrv->remove(dssdev); |
| 4708 | + |
| 4709 | + dss_uninit_device(core.pdev, dssdev); |
| 4710 | + |
| 4711 | + dssdev->driver = NULL; |
| 4712 | + |
| 4713 | + return 0; |
| 4714 | +} |
| 4715 | + |
| 4716 | +int omap_dss_register_driver(struct omap_dss_driver *dssdriver) |
| 4717 | +{ |
| 4718 | + dssdriver->driver.bus = &dss_bus_type; |
| 4719 | + dssdriver->driver.probe = dss_driver_probe; |
| 4720 | + dssdriver->driver.remove = dss_driver_remove; |
| 4721 | + return driver_register(&dssdriver->driver); |
| 4722 | +} |
| 4723 | +EXPORT_SYMBOL(omap_dss_register_driver); |
| 4724 | + |
| 4725 | +void omap_dss_unregister_driver(struct omap_dss_driver *dssdriver) |
| 4726 | +{ |
| 4727 | + driver_unregister(&dssdriver->driver); |
| 4728 | +} |
| 4729 | +EXPORT_SYMBOL(omap_dss_unregister_driver); |
| 4730 | + |
| 4731 | +/* DEVICE */ |
| 4732 | +static void reset_device(struct device *dev, int check) |
| 4733 | +{ |
| 4734 | + u8 *dev_p = (u8 *)dev; |
| 4735 | + u8 *dev_end = dev_p + sizeof(*dev); |
| 4736 | + void *saved_pdata; |
| 4737 | + |
| 4738 | + saved_pdata = dev->platform_data; |
| 4739 | + if (check) { |
| 4740 | + /* |
| 4741 | + * Check if there is any other setting than platform_data |
| 4742 | + * in struct device; warn that these will be reset by our |
| 4743 | + * init. |
| 4744 | + */ |
| 4745 | + dev->platform_data = NULL; |
| 4746 | + while (dev_p < dev_end) { |
| 4747 | + if (*dev_p) { |
| 4748 | + WARN("%s: struct device fields will be " |
| 4749 | + "discarded\n", |
| 4750 | + __func__); |
| 4751 | + break; |
| 4752 | + } |
| 4753 | + dev_p++; |
| 4754 | + } |
| 4755 | + } |
| 4756 | + memset(dev, 0, sizeof(*dev)); |
| 4757 | + dev->platform_data = saved_pdata; |
| 4758 | +} |
| 4759 | + |
| 4760 | + |
| 4761 | +static void omap_dss_dev_release(struct device *dev) |
| 4762 | +{ |
| 4763 | + reset_device(dev, 0); |
| 4764 | +} |
| 4765 | + |
| 4766 | +int omap_dss_register_device(struct omap_dss_device *dssdev) |
| 4767 | +{ |
| 4768 | + static int dev_num; |
| 4769 | + static int panel_num; |
| 4770 | + int r; |
| 4771 | + |
| 4772 | + WARN_ON(!dssdev->driver_name); |
| 4773 | + |
| 4774 | + reset_device(&dssdev->dev, 1); |
| 4775 | + dssdev->dev.bus = &dss_bus_type; |
| 4776 | + dssdev->dev.parent = &dss_bus; |
| 4777 | + dssdev->dev.release = omap_dss_dev_release; |
| 4778 | + dev_set_name(&dssdev->dev, "display%d", dev_num++); |
| 4779 | + r = device_register(&dssdev->dev); |
| 4780 | + if (r) |
| 4781 | + return r; |
| 4782 | + |
| 4783 | + if (dssdev->ctrl.panel) { |
| 4784 | + struct omap_dss_device *panel = dssdev->ctrl.panel; |
| 4785 | + |
| 4786 | + panel->panel.ctrl = dssdev; |
| 4787 | + |
| 4788 | + reset_device(&panel->dev, 1); |
| 4789 | + panel->dev.bus = &dss_bus_type; |
| 4790 | + panel->dev.parent = &dssdev->dev; |
| 4791 | + panel->dev.release = omap_dss_dev_release; |
| 4792 | + dev_set_name(&panel->dev, "panel%d", panel_num++); |
| 4793 | + r = device_register(&panel->dev); |
| 4794 | + if (r) |
| 4795 | + return r; |
| 4796 | + } |
| 4797 | + |
| 4798 | + return 0; |
| 4799 | +} |
| 4800 | + |
| 4801 | +void omap_dss_unregister_device(struct omap_dss_device *dssdev) |
| 4802 | +{ |
| 4803 | + device_unregister(&dssdev->dev); |
| 4804 | + |
| 4805 | + if (dssdev->ctrl.panel) { |
| 4806 | + struct omap_dss_device *panel = dssdev->ctrl.panel; |
| 4807 | + device_unregister(&panel->dev); |
| 4808 | + } |
| 4809 | +} |
| 4810 | + |
| 4811 | +/* BUS */ |
| 4812 | +static int omap_dss_bus_register(void) |
| 4813 | +{ |
| 4814 | + int r; |
| 4815 | + |
| 4816 | + r = bus_register(&dss_bus_type); |
| 4817 | + if (r) { |
| 4818 | + DSSERR("bus register failed\n"); |
| 4819 | + return r; |
| 4820 | + } |
| 4821 | + |
| 4822 | + dev_set_name(&dss_bus, "omapdss"); |
| 4823 | + r = device_register(&dss_bus); |
| 4824 | + if (r) { |
| 4825 | + DSSERR("bus driver register failed\n"); |
| 4826 | + bus_unregister(&dss_bus_type); |
| 4827 | + return r; |
| 4828 | + } |
| 4829 | + |
| 4830 | + return 0; |
| 4831 | +} |
| 4832 | + |
| 4833 | +/* INIT */ |
| 4834 | + |
| 4835 | +#ifdef CONFIG_OMAP2_DSS_MODULE |
| 4836 | +static void omap_dss_bus_unregister(void) |
| 4837 | +{ |
| 4838 | + device_unregister(&dss_bus); |
| 4839 | + |
| 4840 | + bus_unregister(&dss_bus_type); |
| 4841 | +} |
| 4842 | + |
| 4843 | +static int __init omap_dss_init(void) |
| 4844 | +{ |
| 4845 | + int r; |
| 4846 | + |
| 4847 | + r = omap_dss_bus_register(); |
| 4848 | + if (r) |
| 4849 | + return r; |
| 4850 | + |
| 4851 | + r = platform_driver_register(&omap_dss_driver); |
| 4852 | + if (r) { |
| 4853 | + omap_dss_bus_unregister(); |
| 4854 | + return r; |
| 4855 | + } |
| 4856 | + |
| 4857 | + return 0; |
| 4858 | +} |
| 4859 | + |
| 4860 | +static void __exit omap_dss_exit(void) |
| 4861 | +{ |
| 4862 | + platform_driver_unregister(&omap_dss_driver); |
| 4863 | + |
| 4864 | + omap_dss_bus_unregister(); |
| 4865 | +} |
| 4866 | + |
| 4867 | +module_init(omap_dss_init); |
| 4868 | +module_exit(omap_dss_exit); |
| 4869 | +#else |
| 4870 | +static int __init omap_dss_init(void) |
| 4871 | +{ |
| 4872 | + return omap_dss_bus_register(); |
| 4873 | +} |
| 4874 | + |
| 4875 | +static int __init omap_dss_init2(void) |
| 4876 | +{ |
| 4877 | + return platform_driver_register(&omap_dss_driver); |
| 4878 | +} |
| 4879 | + |
| 4880 | +core_initcall(omap_dss_init); |
| 4881 | +device_initcall(omap_dss_init2); |
| 4882 | +#endif |
| 4883 | + |
| 4884 | +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); |
| 4885 | +MODULE_DESCRIPTION("OMAP2/3 Display Subsystem"); |
| 4886 | +MODULE_LICENSE("GPL v2"); |
| 4887 | + |
| 4888 | --- /dev/null |
| 4889 | +++ b/drivers/video/omap2/dss/dispc.c |
| 4890 | @@ -0,0 +1,3182 @@ |
| 4891 | +/* |
| 4892 | + * linux/drivers/video/omap2/dss/dispc.c |
| 4893 | + * |
| 4894 | + * Copyright (C) 2009 Nokia Corporation |
| 4895 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 4896 | + * |
| 4897 | + * Some code and ideas taken from drivers/video/omap/ driver |
| 4898 | + * by Imre Deak. |
| 4899 | + * |
| 4900 | + * This program is free software; you can redistribute it and/or modify it |
| 4901 | + * under the terms of the GNU General Public License version 2 as published by |
| 4902 | + * the Free Software Foundation. |
| 4903 | + * |
| 4904 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 4905 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 4906 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 4907 | + * more details. |
| 4908 | + * |
| 4909 | + * You should have received a copy of the GNU General Public License along with |
| 4910 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 4911 | + */ |
| 4912 | + |
| 4913 | +#define DSS_SUBSYS_NAME "DISPC" |
| 4914 | + |
| 4915 | +#include <linux/kernel.h> |
| 4916 | +#include <linux/dma-mapping.h> |
| 4917 | +#include <linux/vmalloc.h> |
| 4918 | +#include <linux/clk.h> |
| 4919 | +#include <linux/io.h> |
| 4920 | +#include <linux/jiffies.h> |
| 4921 | +#include <linux/seq_file.h> |
| 4922 | +#include <linux/delay.h> |
| 4923 | +#include <linux/workqueue.h> |
| 4924 | + |
| 4925 | +#include <mach/sram.h> |
| 4926 | +#include <mach/board.h> |
| 4927 | +#include <mach/clock.h> |
| 4928 | + |
| 4929 | +#include <mach/display.h> |
| 4930 | + |
| 4931 | +#include "dss.h" |
| 4932 | + |
| 4933 | +/* DISPC */ |
| 4934 | +#define DISPC_BASE 0x48050400 |
| 4935 | + |
| 4936 | +#define DISPC_SZ_REGS SZ_1K |
| 4937 | + |
| 4938 | +struct dispc_reg { u16 idx; }; |
| 4939 | + |
| 4940 | +#define DISPC_REG(idx) ((const struct dispc_reg) { idx }) |
| 4941 | + |
| 4942 | +/* DISPC common */ |
| 4943 | +#define DISPC_REVISION DISPC_REG(0x0000) |
| 4944 | +#define DISPC_SYSCONFIG DISPC_REG(0x0010) |
| 4945 | +#define DISPC_SYSSTATUS DISPC_REG(0x0014) |
| 4946 | +#define DISPC_IRQSTATUS DISPC_REG(0x0018) |
| 4947 | +#define DISPC_IRQENABLE DISPC_REG(0x001C) |
| 4948 | +#define DISPC_CONTROL DISPC_REG(0x0040) |
| 4949 | +#define DISPC_CONFIG DISPC_REG(0x0044) |
| 4950 | +#define DISPC_CAPABLE DISPC_REG(0x0048) |
| 4951 | +#define DISPC_DEFAULT_COLOR0 DISPC_REG(0x004C) |
| 4952 | +#define DISPC_DEFAULT_COLOR1 DISPC_REG(0x0050) |
| 4953 | +#define DISPC_TRANS_COLOR0 DISPC_REG(0x0054) |
| 4954 | +#define DISPC_TRANS_COLOR1 DISPC_REG(0x0058) |
| 4955 | +#define DISPC_LINE_STATUS DISPC_REG(0x005C) |
| 4956 | +#define DISPC_LINE_NUMBER DISPC_REG(0x0060) |
| 4957 | +#define DISPC_TIMING_H DISPC_REG(0x0064) |
| 4958 | +#define DISPC_TIMING_V DISPC_REG(0x0068) |
| 4959 | +#define DISPC_POL_FREQ DISPC_REG(0x006C) |
| 4960 | +#define DISPC_DIVISOR DISPC_REG(0x0070) |
| 4961 | +#define DISPC_GLOBAL_ALPHA DISPC_REG(0x0074) |
| 4962 | +#define DISPC_SIZE_DIG DISPC_REG(0x0078) |
| 4963 | +#define DISPC_SIZE_LCD DISPC_REG(0x007C) |
| 4964 | + |
| 4965 | +/* DISPC GFX plane */ |
| 4966 | +#define DISPC_GFX_BA0 DISPC_REG(0x0080) |
| 4967 | +#define DISPC_GFX_BA1 DISPC_REG(0x0084) |
| 4968 | +#define DISPC_GFX_POSITION DISPC_REG(0x0088) |
| 4969 | +#define DISPC_GFX_SIZE DISPC_REG(0x008C) |
| 4970 | +#define DISPC_GFX_ATTRIBUTES DISPC_REG(0x00A0) |
| 4971 | +#define DISPC_GFX_FIFO_THRESHOLD DISPC_REG(0x00A4) |
| 4972 | +#define DISPC_GFX_FIFO_SIZE_STATUS DISPC_REG(0x00A8) |
| 4973 | +#define DISPC_GFX_ROW_INC DISPC_REG(0x00AC) |
| 4974 | +#define DISPC_GFX_PIXEL_INC DISPC_REG(0x00B0) |
| 4975 | +#define DISPC_GFX_WINDOW_SKIP DISPC_REG(0x00B4) |
| 4976 | +#define DISPC_GFX_TABLE_BA DISPC_REG(0x00B8) |
| 4977 | + |
| 4978 | +#define DISPC_DATA_CYCLE1 DISPC_REG(0x01D4) |
| 4979 | +#define DISPC_DATA_CYCLE2 DISPC_REG(0x01D8) |
| 4980 | +#define DISPC_DATA_CYCLE3 DISPC_REG(0x01DC) |
| 4981 | + |
| 4982 | +#define DISPC_CPR_COEF_R DISPC_REG(0x0220) |
| 4983 | +#define DISPC_CPR_COEF_G DISPC_REG(0x0224) |
| 4984 | +#define DISPC_CPR_COEF_B DISPC_REG(0x0228) |
| 4985 | + |
| 4986 | +#define DISPC_GFX_PRELOAD DISPC_REG(0x022C) |
| 4987 | + |
| 4988 | +/* DISPC Video plane, n = 0 for VID1 and n = 1 for VID2 */ |
| 4989 | +#define DISPC_VID_REG(n, idx) DISPC_REG(0x00BC + (n)*0x90 + idx) |
| 4990 | + |
| 4991 | +#define DISPC_VID_BA0(n) DISPC_VID_REG(n, 0x0000) |
| 4992 | +#define DISPC_VID_BA1(n) DISPC_VID_REG(n, 0x0004) |
| 4993 | +#define DISPC_VID_POSITION(n) DISPC_VID_REG(n, 0x0008) |
| 4994 | +#define DISPC_VID_SIZE(n) DISPC_VID_REG(n, 0x000C) |
| 4995 | +#define DISPC_VID_ATTRIBUTES(n) DISPC_VID_REG(n, 0x0010) |
| 4996 | +#define DISPC_VID_FIFO_THRESHOLD(n) DISPC_VID_REG(n, 0x0014) |
| 4997 | +#define DISPC_VID_FIFO_SIZE_STATUS(n) DISPC_VID_REG(n, 0x0018) |
| 4998 | +#define DISPC_VID_ROW_INC(n) DISPC_VID_REG(n, 0x001C) |
| 4999 | +#define DISPC_VID_PIXEL_INC(n) DISPC_VID_REG(n, 0x0020) |
| 5000 | +#define DISPC_VID_FIR(n) DISPC_VID_REG(n, 0x0024) |
| 5001 | +#define DISPC_VID_PICTURE_SIZE(n) DISPC_VID_REG(n, 0x0028) |
| 5002 | +#define DISPC_VID_ACCU0(n) DISPC_VID_REG(n, 0x002C) |
| 5003 | +#define DISPC_VID_ACCU1(n) DISPC_VID_REG(n, 0x0030) |
| 5004 | + |
| 5005 | +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ |
| 5006 | +#define DISPC_VID_FIR_COEF_H(n, i) DISPC_REG(0x00F0 + (n)*0x90 + (i)*0x8) |
| 5007 | +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ |
| 5008 | +#define DISPC_VID_FIR_COEF_HV(n, i) DISPC_REG(0x00F4 + (n)*0x90 + (i)*0x8) |
| 5009 | +/* coef index i = {0, 1, 2, 3, 4} */ |
| 5010 | +#define DISPC_VID_CONV_COEF(n, i) DISPC_REG(0x0130 + (n)*0x90 + (i)*0x4) |
| 5011 | +/* coef index i = {0, 1, 2, 3, 4, 5, 6, 7} */ |
| 5012 | +#define DISPC_VID_FIR_COEF_V(n, i) DISPC_REG(0x01E0 + (n)*0x20 + (i)*0x4) |
| 5013 | + |
| 5014 | +#define DISPC_VID_PRELOAD(n) DISPC_REG(0x230 + (n)*0x04) |
| 5015 | + |
| 5016 | + |
| 5017 | +#define DISPC_IRQ_MASK_ERROR (DISPC_IRQ_GFX_FIFO_UNDERFLOW | \ |
| 5018 | + DISPC_IRQ_OCP_ERR | \ |
| 5019 | + DISPC_IRQ_VID1_FIFO_UNDERFLOW | \ |
| 5020 | + DISPC_IRQ_VID2_FIFO_UNDERFLOW | \ |
| 5021 | + DISPC_IRQ_SYNC_LOST | \ |
| 5022 | + DISPC_IRQ_SYNC_LOST_DIGIT) |
| 5023 | + |
| 5024 | +#define DISPC_MAX_NR_ISRS 8 |
| 5025 | + |
| 5026 | +struct omap_dispc_isr_data { |
| 5027 | + omap_dispc_isr_t isr; |
| 5028 | + void *arg; |
| 5029 | + u32 mask; |
| 5030 | +}; |
| 5031 | + |
| 5032 | +#define REG_GET(idx, start, end) \ |
| 5033 | + FLD_GET(dispc_read_reg(idx), start, end) |
| 5034 | + |
| 5035 | +#define REG_FLD_MOD(idx, val, start, end) \ |
| 5036 | + dispc_write_reg(idx, FLD_MOD(dispc_read_reg(idx), val, start, end)) |
| 5037 | + |
| 5038 | +static const struct dispc_reg dispc_reg_att[] = { DISPC_GFX_ATTRIBUTES, |
| 5039 | + DISPC_VID_ATTRIBUTES(0), |
| 5040 | + DISPC_VID_ATTRIBUTES(1) }; |
| 5041 | + |
| 5042 | +static struct { |
| 5043 | + void __iomem *base; |
| 5044 | + |
| 5045 | + struct clk *dpll4_m4_ck; |
| 5046 | + |
| 5047 | + unsigned long cache_req_pck; |
| 5048 | + unsigned long cache_prate; |
| 5049 | + struct dispc_clock_info cache_cinfo; |
| 5050 | + |
| 5051 | + u32 fifo_size[3]; |
| 5052 | + |
| 5053 | + spinlock_t irq_lock; |
| 5054 | + u32 irq_error_mask; |
| 5055 | + struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; |
| 5056 | + u32 error_irqs; |
| 5057 | + struct work_struct error_work; |
| 5058 | + |
| 5059 | + u32 ctx[DISPC_SZ_REGS / sizeof(u32)]; |
| 5060 | +} dispc; |
| 5061 | + |
| 5062 | +static void _omap_dispc_set_irqs(void); |
| 5063 | + |
| 5064 | +static inline void dispc_write_reg(const struct dispc_reg idx, u32 val) |
| 5065 | +{ |
| 5066 | + __raw_writel(val, dispc.base + idx.idx); |
| 5067 | +} |
| 5068 | + |
| 5069 | +static inline u32 dispc_read_reg(const struct dispc_reg idx) |
| 5070 | +{ |
| 5071 | + return __raw_readl(dispc.base + idx.idx); |
| 5072 | +} |
| 5073 | + |
| 5074 | +#define SR(reg) \ |
| 5075 | + dispc.ctx[(DISPC_##reg).idx / sizeof(u32)] = dispc_read_reg(DISPC_##reg) |
| 5076 | +#define RR(reg) \ |
| 5077 | + dispc_write_reg(DISPC_##reg, dispc.ctx[(DISPC_##reg).idx / sizeof(u32)]) |
| 5078 | + |
| 5079 | +void dispc_save_context(void) |
| 5080 | +{ |
| 5081 | + if (cpu_is_omap24xx()) |
| 5082 | + return; |
| 5083 | + |
| 5084 | + SR(SYSCONFIG); |
| 5085 | + SR(IRQENABLE); |
| 5086 | + SR(CONTROL); |
| 5087 | + SR(CONFIG); |
| 5088 | + SR(DEFAULT_COLOR0); |
| 5089 | + SR(DEFAULT_COLOR1); |
| 5090 | + SR(TRANS_COLOR0); |
| 5091 | + SR(TRANS_COLOR1); |
| 5092 | + SR(LINE_NUMBER); |
| 5093 | + SR(TIMING_H); |
| 5094 | + SR(TIMING_V); |
| 5095 | + SR(POL_FREQ); |
| 5096 | + SR(DIVISOR); |
| 5097 | + SR(GLOBAL_ALPHA); |
| 5098 | + SR(SIZE_DIG); |
| 5099 | + SR(SIZE_LCD); |
| 5100 | + |
| 5101 | + SR(GFX_BA0); |
| 5102 | + SR(GFX_BA1); |
| 5103 | + SR(GFX_POSITION); |
| 5104 | + SR(GFX_SIZE); |
| 5105 | + SR(GFX_ATTRIBUTES); |
| 5106 | + SR(GFX_FIFO_THRESHOLD); |
| 5107 | + SR(GFX_ROW_INC); |
| 5108 | + SR(GFX_PIXEL_INC); |
| 5109 | + SR(GFX_WINDOW_SKIP); |
| 5110 | + SR(GFX_TABLE_BA); |
| 5111 | + |
| 5112 | + SR(DATA_CYCLE1); |
| 5113 | + SR(DATA_CYCLE2); |
| 5114 | + SR(DATA_CYCLE3); |
| 5115 | + |
| 5116 | + SR(CPR_COEF_R); |
| 5117 | + SR(CPR_COEF_G); |
| 5118 | + SR(CPR_COEF_B); |
| 5119 | + |
| 5120 | + SR(GFX_PRELOAD); |
| 5121 | + |
| 5122 | + /* VID1 */ |
| 5123 | + SR(VID_BA0(0)); |
| 5124 | + SR(VID_BA1(0)); |
| 5125 | + SR(VID_POSITION(0)); |
| 5126 | + SR(VID_SIZE(0)); |
| 5127 | + SR(VID_ATTRIBUTES(0)); |
| 5128 | + SR(VID_FIFO_THRESHOLD(0)); |
| 5129 | + SR(VID_ROW_INC(0)); |
| 5130 | + SR(VID_PIXEL_INC(0)); |
| 5131 | + SR(VID_FIR(0)); |
| 5132 | + SR(VID_PICTURE_SIZE(0)); |
| 5133 | + SR(VID_ACCU0(0)); |
| 5134 | + SR(VID_ACCU1(0)); |
| 5135 | + |
| 5136 | + SR(VID_FIR_COEF_H(0, 0)); |
| 5137 | + SR(VID_FIR_COEF_H(0, 1)); |
| 5138 | + SR(VID_FIR_COEF_H(0, 2)); |
| 5139 | + SR(VID_FIR_COEF_H(0, 3)); |
| 5140 | + SR(VID_FIR_COEF_H(0, 4)); |
| 5141 | + SR(VID_FIR_COEF_H(0, 5)); |
| 5142 | + SR(VID_FIR_COEF_H(0, 6)); |
| 5143 | + SR(VID_FIR_COEF_H(0, 7)); |
| 5144 | + |
| 5145 | + SR(VID_FIR_COEF_HV(0, 0)); |
| 5146 | + SR(VID_FIR_COEF_HV(0, 1)); |
| 5147 | + SR(VID_FIR_COEF_HV(0, 2)); |
| 5148 | + SR(VID_FIR_COEF_HV(0, 3)); |
| 5149 | + SR(VID_FIR_COEF_HV(0, 4)); |
| 5150 | + SR(VID_FIR_COEF_HV(0, 5)); |
| 5151 | + SR(VID_FIR_COEF_HV(0, 6)); |
| 5152 | + SR(VID_FIR_COEF_HV(0, 7)); |
| 5153 | + |
| 5154 | + SR(VID_CONV_COEF(0, 0)); |
| 5155 | + SR(VID_CONV_COEF(0, 1)); |
| 5156 | + SR(VID_CONV_COEF(0, 2)); |
| 5157 | + SR(VID_CONV_COEF(0, 3)); |
| 5158 | + SR(VID_CONV_COEF(0, 4)); |
| 5159 | + |
| 5160 | + SR(VID_FIR_COEF_V(0, 0)); |
| 5161 | + SR(VID_FIR_COEF_V(0, 1)); |
| 5162 | + SR(VID_FIR_COEF_V(0, 2)); |
| 5163 | + SR(VID_FIR_COEF_V(0, 3)); |
| 5164 | + SR(VID_FIR_COEF_V(0, 4)); |
| 5165 | + SR(VID_FIR_COEF_V(0, 5)); |
| 5166 | + SR(VID_FIR_COEF_V(0, 6)); |
| 5167 | + SR(VID_FIR_COEF_V(0, 7)); |
| 5168 | + |
| 5169 | + SR(VID_PRELOAD(0)); |
| 5170 | + |
| 5171 | + /* VID2 */ |
| 5172 | + SR(VID_BA0(1)); |
| 5173 | + SR(VID_BA1(1)); |
| 5174 | + SR(VID_POSITION(1)); |
| 5175 | + SR(VID_SIZE(1)); |
| 5176 | + SR(VID_ATTRIBUTES(1)); |
| 5177 | + SR(VID_FIFO_THRESHOLD(1)); |
| 5178 | + SR(VID_ROW_INC(1)); |
| 5179 | + SR(VID_PIXEL_INC(1)); |
| 5180 | + SR(VID_FIR(1)); |
| 5181 | + SR(VID_PICTURE_SIZE(1)); |
| 5182 | + SR(VID_ACCU0(1)); |
| 5183 | + SR(VID_ACCU1(1)); |
| 5184 | + |
| 5185 | + SR(VID_FIR_COEF_H(1, 0)); |
| 5186 | + SR(VID_FIR_COEF_H(1, 1)); |
| 5187 | + SR(VID_FIR_COEF_H(1, 2)); |
| 5188 | + SR(VID_FIR_COEF_H(1, 3)); |
| 5189 | + SR(VID_FIR_COEF_H(1, 4)); |
| 5190 | + SR(VID_FIR_COEF_H(1, 5)); |
| 5191 | + SR(VID_FIR_COEF_H(1, 6)); |
| 5192 | + SR(VID_FIR_COEF_H(1, 7)); |
| 5193 | + |
| 5194 | + SR(VID_FIR_COEF_HV(1, 0)); |
| 5195 | + SR(VID_FIR_COEF_HV(1, 1)); |
| 5196 | + SR(VID_FIR_COEF_HV(1, 2)); |
| 5197 | + SR(VID_FIR_COEF_HV(1, 3)); |
| 5198 | + SR(VID_FIR_COEF_HV(1, 4)); |
| 5199 | + SR(VID_FIR_COEF_HV(1, 5)); |
| 5200 | + SR(VID_FIR_COEF_HV(1, 6)); |
| 5201 | + SR(VID_FIR_COEF_HV(1, 7)); |
| 5202 | + |
| 5203 | + SR(VID_CONV_COEF(1, 0)); |
| 5204 | + SR(VID_CONV_COEF(1, 1)); |
| 5205 | + SR(VID_CONV_COEF(1, 2)); |
| 5206 | + SR(VID_CONV_COEF(1, 3)); |
| 5207 | + SR(VID_CONV_COEF(1, 4)); |
| 5208 | + |
| 5209 | + SR(VID_FIR_COEF_V(1, 0)); |
| 5210 | + SR(VID_FIR_COEF_V(1, 1)); |
| 5211 | + SR(VID_FIR_COEF_V(1, 2)); |
| 5212 | + SR(VID_FIR_COEF_V(1, 3)); |
| 5213 | + SR(VID_FIR_COEF_V(1, 4)); |
| 5214 | + SR(VID_FIR_COEF_V(1, 5)); |
| 5215 | + SR(VID_FIR_COEF_V(1, 6)); |
| 5216 | + SR(VID_FIR_COEF_V(1, 7)); |
| 5217 | + |
| 5218 | + SR(VID_PRELOAD(1)); |
| 5219 | +} |
| 5220 | + |
| 5221 | +void dispc_restore_context(void) |
| 5222 | +{ |
| 5223 | + RR(SYSCONFIG); |
| 5224 | + RR(IRQENABLE); |
| 5225 | + /*RR(CONTROL);*/ |
| 5226 | + RR(CONFIG); |
| 5227 | + RR(DEFAULT_COLOR0); |
| 5228 | + RR(DEFAULT_COLOR1); |
| 5229 | + RR(TRANS_COLOR0); |
| 5230 | + RR(TRANS_COLOR1); |
| 5231 | + RR(LINE_NUMBER); |
| 5232 | + RR(TIMING_H); |
| 5233 | + RR(TIMING_V); |
| 5234 | + RR(POL_FREQ); |
| 5235 | + RR(DIVISOR); |
| 5236 | + RR(GLOBAL_ALPHA); |
| 5237 | + RR(SIZE_DIG); |
| 5238 | + RR(SIZE_LCD); |
| 5239 | + |
| 5240 | + RR(GFX_BA0); |
| 5241 | + RR(GFX_BA1); |
| 5242 | + RR(GFX_POSITION); |
| 5243 | + RR(GFX_SIZE); |
| 5244 | + RR(GFX_ATTRIBUTES); |
| 5245 | + RR(GFX_FIFO_THRESHOLD); |
| 5246 | + RR(GFX_ROW_INC); |
| 5247 | + RR(GFX_PIXEL_INC); |
| 5248 | + RR(GFX_WINDOW_SKIP); |
| 5249 | + RR(GFX_TABLE_BA); |
| 5250 | + |
| 5251 | + RR(DATA_CYCLE1); |
| 5252 | + RR(DATA_CYCLE2); |
| 5253 | + RR(DATA_CYCLE3); |
| 5254 | + |
| 5255 | + RR(CPR_COEF_R); |
| 5256 | + RR(CPR_COEF_G); |
| 5257 | + RR(CPR_COEF_B); |
| 5258 | + |
| 5259 | + RR(GFX_PRELOAD); |
| 5260 | + |
| 5261 | + /* VID1 */ |
| 5262 | + RR(VID_BA0(0)); |
| 5263 | + RR(VID_BA1(0)); |
| 5264 | + RR(VID_POSITION(0)); |
| 5265 | + RR(VID_SIZE(0)); |
| 5266 | + RR(VID_ATTRIBUTES(0)); |
| 5267 | + RR(VID_FIFO_THRESHOLD(0)); |
| 5268 | + RR(VID_ROW_INC(0)); |
| 5269 | + RR(VID_PIXEL_INC(0)); |
| 5270 | + RR(VID_FIR(0)); |
| 5271 | + RR(VID_PICTURE_SIZE(0)); |
| 5272 | + RR(VID_ACCU0(0)); |
| 5273 | + RR(VID_ACCU1(0)); |
| 5274 | + |
| 5275 | + RR(VID_FIR_COEF_H(0, 0)); |
| 5276 | + RR(VID_FIR_COEF_H(0, 1)); |
| 5277 | + RR(VID_FIR_COEF_H(0, 2)); |
| 5278 | + RR(VID_FIR_COEF_H(0, 3)); |
| 5279 | + RR(VID_FIR_COEF_H(0, 4)); |
| 5280 | + RR(VID_FIR_COEF_H(0, 5)); |
| 5281 | + RR(VID_FIR_COEF_H(0, 6)); |
| 5282 | + RR(VID_FIR_COEF_H(0, 7)); |
| 5283 | + |
| 5284 | + RR(VID_FIR_COEF_HV(0, 0)); |
| 5285 | + RR(VID_FIR_COEF_HV(0, 1)); |
| 5286 | + RR(VID_FIR_COEF_HV(0, 2)); |
| 5287 | + RR(VID_FIR_COEF_HV(0, 3)); |
| 5288 | + RR(VID_FIR_COEF_HV(0, 4)); |
| 5289 | + RR(VID_FIR_COEF_HV(0, 5)); |
| 5290 | + RR(VID_FIR_COEF_HV(0, 6)); |
| 5291 | + RR(VID_FIR_COEF_HV(0, 7)); |
| 5292 | + |
| 5293 | + RR(VID_CONV_COEF(0, 0)); |
| 5294 | + RR(VID_CONV_COEF(0, 1)); |
| 5295 | + RR(VID_CONV_COEF(0, 2)); |
| 5296 | + RR(VID_CONV_COEF(0, 3)); |
| 5297 | + RR(VID_CONV_COEF(0, 4)); |
| 5298 | + |
| 5299 | + RR(VID_FIR_COEF_V(0, 0)); |
| 5300 | + RR(VID_FIR_COEF_V(0, 1)); |
| 5301 | + RR(VID_FIR_COEF_V(0, 2)); |
| 5302 | + RR(VID_FIR_COEF_V(0, 3)); |
| 5303 | + RR(VID_FIR_COEF_V(0, 4)); |
| 5304 | + RR(VID_FIR_COEF_V(0, 5)); |
| 5305 | + RR(VID_FIR_COEF_V(0, 6)); |
| 5306 | + RR(VID_FIR_COEF_V(0, 7)); |
| 5307 | + |
| 5308 | + RR(VID_PRELOAD(0)); |
| 5309 | + |
| 5310 | + /* VID2 */ |
| 5311 | + RR(VID_BA0(1)); |
| 5312 | + RR(VID_BA1(1)); |
| 5313 | + RR(VID_POSITION(1)); |
| 5314 | + RR(VID_SIZE(1)); |
| 5315 | + RR(VID_ATTRIBUTES(1)); |
| 5316 | + RR(VID_FIFO_THRESHOLD(1)); |
| 5317 | + RR(VID_ROW_INC(1)); |
| 5318 | + RR(VID_PIXEL_INC(1)); |
| 5319 | + RR(VID_FIR(1)); |
| 5320 | + RR(VID_PICTURE_SIZE(1)); |
| 5321 | + RR(VID_ACCU0(1)); |
| 5322 | + RR(VID_ACCU1(1)); |
| 5323 | + |
| 5324 | + RR(VID_FIR_COEF_H(1, 0)); |
| 5325 | + RR(VID_FIR_COEF_H(1, 1)); |
| 5326 | + RR(VID_FIR_COEF_H(1, 2)); |
| 5327 | + RR(VID_FIR_COEF_H(1, 3)); |
| 5328 | + RR(VID_FIR_COEF_H(1, 4)); |
| 5329 | + RR(VID_FIR_COEF_H(1, 5)); |
| 5330 | + RR(VID_FIR_COEF_H(1, 6)); |
| 5331 | + RR(VID_FIR_COEF_H(1, 7)); |
| 5332 | + |
| 5333 | + RR(VID_FIR_COEF_HV(1, 0)); |
| 5334 | + RR(VID_FIR_COEF_HV(1, 1)); |
| 5335 | + RR(VID_FIR_COEF_HV(1, 2)); |
| 5336 | + RR(VID_FIR_COEF_HV(1, 3)); |
| 5337 | + RR(VID_FIR_COEF_HV(1, 4)); |
| 5338 | + RR(VID_FIR_COEF_HV(1, 5)); |
| 5339 | + RR(VID_FIR_COEF_HV(1, 6)); |
| 5340 | + RR(VID_FIR_COEF_HV(1, 7)); |
| 5341 | + |
| 5342 | + RR(VID_CONV_COEF(1, 0)); |
| 5343 | + RR(VID_CONV_COEF(1, 1)); |
| 5344 | + RR(VID_CONV_COEF(1, 2)); |
| 5345 | + RR(VID_CONV_COEF(1, 3)); |
| 5346 | + RR(VID_CONV_COEF(1, 4)); |
| 5347 | + |
| 5348 | + RR(VID_FIR_COEF_V(1, 0)); |
| 5349 | + RR(VID_FIR_COEF_V(1, 1)); |
| 5350 | + RR(VID_FIR_COEF_V(1, 2)); |
| 5351 | + RR(VID_FIR_COEF_V(1, 3)); |
| 5352 | + RR(VID_FIR_COEF_V(1, 4)); |
| 5353 | + RR(VID_FIR_COEF_V(1, 5)); |
| 5354 | + RR(VID_FIR_COEF_V(1, 6)); |
| 5355 | + RR(VID_FIR_COEF_V(1, 7)); |
| 5356 | + |
| 5357 | + RR(VID_PRELOAD(1)); |
| 5358 | + |
| 5359 | + /* enable last, because LCD & DIGIT enable are here */ |
| 5360 | + RR(CONTROL); |
| 5361 | +} |
| 5362 | + |
| 5363 | +#undef SR |
| 5364 | +#undef RR |
| 5365 | + |
| 5366 | +static inline void enable_clocks(bool enable) |
| 5367 | +{ |
| 5368 | + if (enable) |
| 5369 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 5370 | + else |
| 5371 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 5372 | +} |
| 5373 | + |
| 5374 | +bool dispc_go_busy(enum omap_channel channel) |
| 5375 | +{ |
| 5376 | + int bit; |
| 5377 | + |
| 5378 | + if (channel == OMAP_DSS_CHANNEL_LCD) |
| 5379 | + bit = 5; /* GOLCD */ |
| 5380 | + else |
| 5381 | + bit = 6; /* GODIGIT */ |
| 5382 | + |
| 5383 | + return REG_GET(DISPC_CONTROL, bit, bit) == 1; |
| 5384 | +} |
| 5385 | + |
| 5386 | +void dispc_go(enum omap_channel channel) |
| 5387 | +{ |
| 5388 | + int bit; |
| 5389 | + |
| 5390 | + enable_clocks(1); |
| 5391 | + |
| 5392 | + if (channel == OMAP_DSS_CHANNEL_LCD) |
| 5393 | + bit = 0; /* LCDENABLE */ |
| 5394 | + else |
| 5395 | + bit = 1; /* DIGITALENABLE */ |
| 5396 | + |
| 5397 | + /* if the channel is not enabled, we don't need GO */ |
| 5398 | + if (REG_GET(DISPC_CONTROL, bit, bit) == 0) |
| 5399 | + goto end; |
| 5400 | + |
| 5401 | + if (channel == OMAP_DSS_CHANNEL_LCD) |
| 5402 | + bit = 5; /* GOLCD */ |
| 5403 | + else |
| 5404 | + bit = 6; /* GODIGIT */ |
| 5405 | + |
| 5406 | + if (REG_GET(DISPC_CONTROL, bit, bit) == 1) { |
| 5407 | + DSSERR("GO bit not down for channel %d\n", channel); |
| 5408 | + goto end; |
| 5409 | + } |
| 5410 | + |
| 5411 | + DSSDBG("GO %s\n", channel == OMAP_DSS_CHANNEL_LCD ? "LCD" : "DIGIT"); |
| 5412 | + |
| 5413 | + REG_FLD_MOD(DISPC_CONTROL, 1, bit, bit); |
| 5414 | +end: |
| 5415 | + enable_clocks(0); |
| 5416 | +} |
| 5417 | + |
| 5418 | +static void _dispc_write_firh_reg(enum omap_plane plane, int reg, u32 value) |
| 5419 | +{ |
| 5420 | + BUG_ON(plane == OMAP_DSS_GFX); |
| 5421 | + |
| 5422 | + dispc_write_reg(DISPC_VID_FIR_COEF_H(plane-1, reg), value); |
| 5423 | +} |
| 5424 | + |
| 5425 | +static void _dispc_write_firhv_reg(enum omap_plane plane, int reg, u32 value) |
| 5426 | +{ |
| 5427 | + BUG_ON(plane == OMAP_DSS_GFX); |
| 5428 | + |
| 5429 | + dispc_write_reg(DISPC_VID_FIR_COEF_HV(plane-1, reg), value); |
| 5430 | +} |
| 5431 | + |
| 5432 | +static void _dispc_write_firv_reg(enum omap_plane plane, int reg, u32 value) |
| 5433 | +{ |
| 5434 | + BUG_ON(plane == OMAP_DSS_GFX); |
| 5435 | + |
| 5436 | + dispc_write_reg(DISPC_VID_FIR_COEF_V(plane-1, reg), value); |
| 5437 | +} |
| 5438 | + |
| 5439 | +static void _dispc_set_scale_coef(enum omap_plane plane, int hscaleup, |
| 5440 | + int vscaleup, int five_taps) |
| 5441 | +{ |
| 5442 | + /* Coefficients for horizontal up-sampling */ |
| 5443 | + static const u32 coef_hup[8] = { |
| 5444 | + 0x00800000, |
| 5445 | + 0x0D7CF800, |
| 5446 | + 0x1E70F5FF, |
| 5447 | + 0x335FF5FE, |
| 5448 | + 0xF74949F7, |
| 5449 | + 0xF55F33FB, |
| 5450 | + 0xF5701EFE, |
| 5451 | + 0xF87C0DFF, |
| 5452 | + }; |
| 5453 | + |
| 5454 | + /* Coefficients for horizontal down-sampling */ |
| 5455 | + static const u32 coef_hdown[8] = { |
| 5456 | + 0x24382400, |
| 5457 | + 0x28371FFE, |
| 5458 | + 0x2C361BFB, |
| 5459 | + 0x303516F9, |
| 5460 | + 0x11343311, |
| 5461 | + 0x1635300C, |
| 5462 | + 0x1B362C08, |
| 5463 | + 0x1F372804, |
| 5464 | + }; |
| 5465 | + |
| 5466 | + /* Coefficients for horizontal and vertical up-sampling */ |
| 5467 | + static const u32 coef_hvup[2][8] = { |
| 5468 | + { |
| 5469 | + 0x00800000, |
| 5470 | + 0x037B02FF, |
| 5471 | + 0x0C6F05FE, |
| 5472 | + 0x205907FB, |
| 5473 | + 0x00404000, |
| 5474 | + 0x075920FE, |
| 5475 | + 0x056F0CFF, |
| 5476 | + 0x027B0300, |
| 5477 | + }, |
| 5478 | + { |
| 5479 | + 0x00800000, |
| 5480 | + 0x0D7CF8FF, |
| 5481 | + 0x1E70F5FE, |
| 5482 | + 0x335FF5FB, |
| 5483 | + 0xF7404000, |
| 5484 | + 0xF55F33FE, |
| 5485 | + 0xF5701EFF, |
| 5486 | + 0xF87C0D00, |
| 5487 | + }, |
| 5488 | + }; |
| 5489 | + |
| 5490 | + /* Coefficients for horizontal and vertical down-sampling */ |
| 5491 | + static const u32 coef_hvdown[2][8] = { |
| 5492 | + { |
| 5493 | + 0x24382400, |
| 5494 | + 0x28391F04, |
| 5495 | + 0x2D381B08, |
| 5496 | + 0x3237170C, |
| 5497 | + 0x123737F7, |
| 5498 | + 0x173732F9, |
| 5499 | + 0x1B382DFB, |
| 5500 | + 0x1F3928FE, |
| 5501 | + }, |
| 5502 | + { |
| 5503 | + 0x24382400, |
| 5504 | + 0x28371F04, |
| 5505 | + 0x2C361B08, |
| 5506 | + 0x3035160C, |
| 5507 | + 0x113433F7, |
| 5508 | + 0x163530F9, |
| 5509 | + 0x1B362CFB, |
| 5510 | + 0x1F3728FE, |
| 5511 | + }, |
| 5512 | + }; |
| 5513 | + |
| 5514 | + /* Coefficients for vertical up-sampling */ |
| 5515 | + static const u32 coef_vup[8] = { |
| 5516 | + 0x00000000, |
| 5517 | + 0x0000FF00, |
| 5518 | + 0x0000FEFF, |
| 5519 | + 0x0000FBFE, |
| 5520 | + 0x000000F7, |
| 5521 | + 0x0000FEFB, |
| 5522 | + 0x0000FFFE, |
| 5523 | + 0x000000FF, |
| 5524 | + }; |
| 5525 | + |
| 5526 | + |
| 5527 | + /* Coefficients for vertical down-sampling */ |
| 5528 | + static const u32 coef_vdown[8] = { |
| 5529 | + 0x00000000, |
| 5530 | + 0x000004FE, |
| 5531 | + 0x000008FB, |
| 5532 | + 0x00000CF9, |
| 5533 | + 0x0000F711, |
| 5534 | + 0x0000F90C, |
| 5535 | + 0x0000FB08, |
| 5536 | + 0x0000FE04, |
| 5537 | + }; |
| 5538 | + |
| 5539 | + const u32 *h_coef; |
| 5540 | + const u32 *hv_coef; |
| 5541 | + const u32 *hv_coef_mod; |
| 5542 | + const u32 *v_coef; |
| 5543 | + int i; |
| 5544 | + |
| 5545 | + if (hscaleup) |
| 5546 | + h_coef = coef_hup; |
| 5547 | + else |
| 5548 | + h_coef = coef_hdown; |
| 5549 | + |
| 5550 | + if (vscaleup) { |
| 5551 | + hv_coef = coef_hvup[five_taps]; |
| 5552 | + v_coef = coef_vup; |
| 5553 | + |
| 5554 | + if (hscaleup) |
| 5555 | + hv_coef_mod = NULL; |
| 5556 | + else |
| 5557 | + hv_coef_mod = coef_hvdown[five_taps]; |
| 5558 | + } else { |
| 5559 | + hv_coef = coef_hvdown[five_taps]; |
| 5560 | + v_coef = coef_vdown; |
| 5561 | + |
| 5562 | + if (hscaleup) |
| 5563 | + hv_coef_mod = coef_hvup[five_taps]; |
| 5564 | + else |
| 5565 | + hv_coef_mod = NULL; |
| 5566 | + } |
| 5567 | + |
| 5568 | + for (i = 0; i < 8; i++) { |
| 5569 | + u32 h, hv; |
| 5570 | + |
| 5571 | + h = h_coef[i]; |
| 5572 | + |
| 5573 | + hv = hv_coef[i]; |
| 5574 | + |
| 5575 | + if (hv_coef_mod) { |
| 5576 | + hv &= 0xffffff00; |
| 5577 | + hv |= (hv_coef_mod[i] & 0xff); |
| 5578 | + } |
| 5579 | + |
| 5580 | + _dispc_write_firh_reg(plane, i, h); |
| 5581 | + _dispc_write_firhv_reg(plane, i, hv); |
| 5582 | + } |
| 5583 | + |
| 5584 | + if (!five_taps) |
| 5585 | + return; |
| 5586 | + |
| 5587 | + for (i = 0; i < 8; i++) { |
| 5588 | + u32 v; |
| 5589 | + v = v_coef[i]; |
| 5590 | + _dispc_write_firv_reg(plane, i, v); |
| 5591 | + } |
| 5592 | +} |
| 5593 | + |
| 5594 | +static void _dispc_setup_color_conv_coef(void) |
| 5595 | +{ |
| 5596 | + const struct color_conv_coef { |
| 5597 | + int ry, rcr, rcb, gy, gcr, gcb, by, bcr, bcb; |
| 5598 | + int full_range; |
| 5599 | + } ctbl_bt601_5 = { |
| 5600 | + 298, 409, 0, 298, -208, -100, 298, 0, 517, 0, |
| 5601 | + }; |
| 5602 | + |
| 5603 | + const struct color_conv_coef *ct; |
| 5604 | + |
| 5605 | +#define CVAL(x, y) (FLD_VAL(x, 26, 16) | FLD_VAL(y, 10, 0)) |
| 5606 | + |
| 5607 | + ct = &ctbl_bt601_5; |
| 5608 | + |
| 5609 | + dispc_write_reg(DISPC_VID_CONV_COEF(0, 0), CVAL(ct->rcr, ct->ry)); |
| 5610 | + dispc_write_reg(DISPC_VID_CONV_COEF(0, 1), CVAL(ct->gy, ct->rcb)); |
| 5611 | + dispc_write_reg(DISPC_VID_CONV_COEF(0, 2), CVAL(ct->gcb, ct->gcr)); |
| 5612 | + dispc_write_reg(DISPC_VID_CONV_COEF(0, 3), CVAL(ct->bcr, ct->by)); |
| 5613 | + dispc_write_reg(DISPC_VID_CONV_COEF(0, 4), CVAL(0, ct->bcb)); |
| 5614 | + |
| 5615 | + dispc_write_reg(DISPC_VID_CONV_COEF(1, 0), CVAL(ct->rcr, ct->ry)); |
| 5616 | + dispc_write_reg(DISPC_VID_CONV_COEF(1, 1), CVAL(ct->gy, ct->rcb)); |
| 5617 | + dispc_write_reg(DISPC_VID_CONV_COEF(1, 2), CVAL(ct->gcb, ct->gcr)); |
| 5618 | + dispc_write_reg(DISPC_VID_CONV_COEF(1, 3), CVAL(ct->bcr, ct->by)); |
| 5619 | + dispc_write_reg(DISPC_VID_CONV_COEF(1, 4), CVAL(0, ct->bcb)); |
| 5620 | + |
| 5621 | +#undef CVAL |
| 5622 | + |
| 5623 | + REG_FLD_MOD(DISPC_VID_ATTRIBUTES(0), ct->full_range, 11, 11); |
| 5624 | + REG_FLD_MOD(DISPC_VID_ATTRIBUTES(1), ct->full_range, 11, 11); |
| 5625 | +} |
| 5626 | + |
| 5627 | + |
| 5628 | +static void _dispc_set_plane_ba0(enum omap_plane plane, u32 paddr) |
| 5629 | +{ |
| 5630 | + const struct dispc_reg ba0_reg[] = { DISPC_GFX_BA0, |
| 5631 | + DISPC_VID_BA0(0), |
| 5632 | + DISPC_VID_BA0(1) }; |
| 5633 | + |
| 5634 | + dispc_write_reg(ba0_reg[plane], paddr); |
| 5635 | +} |
| 5636 | + |
| 5637 | +static void _dispc_set_plane_ba1(enum omap_plane plane, u32 paddr) |
| 5638 | +{ |
| 5639 | + const struct dispc_reg ba1_reg[] = { DISPC_GFX_BA1, |
| 5640 | + DISPC_VID_BA1(0), |
| 5641 | + DISPC_VID_BA1(1) }; |
| 5642 | + |
| 5643 | + dispc_write_reg(ba1_reg[plane], paddr); |
| 5644 | +} |
| 5645 | + |
| 5646 | +static void _dispc_set_plane_pos(enum omap_plane plane, int x, int y) |
| 5647 | +{ |
| 5648 | + const struct dispc_reg pos_reg[] = { DISPC_GFX_POSITION, |
| 5649 | + DISPC_VID_POSITION(0), |
| 5650 | + DISPC_VID_POSITION(1) }; |
| 5651 | + |
| 5652 | + u32 val = FLD_VAL(y, 26, 16) | FLD_VAL(x, 10, 0); |
| 5653 | + dispc_write_reg(pos_reg[plane], val); |
| 5654 | +} |
| 5655 | + |
| 5656 | +static void _dispc_set_pic_size(enum omap_plane plane, int width, int height) |
| 5657 | +{ |
| 5658 | + const struct dispc_reg siz_reg[] = { DISPC_GFX_SIZE, |
| 5659 | + DISPC_VID_PICTURE_SIZE(0), |
| 5660 | + DISPC_VID_PICTURE_SIZE(1) }; |
| 5661 | + u32 val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); |
| 5662 | + dispc_write_reg(siz_reg[plane], val); |
| 5663 | +} |
| 5664 | + |
| 5665 | +static void _dispc_set_vid_size(enum omap_plane plane, int width, int height) |
| 5666 | +{ |
| 5667 | + u32 val; |
| 5668 | + const struct dispc_reg vsi_reg[] = { DISPC_VID_SIZE(0), |
| 5669 | + DISPC_VID_SIZE(1) }; |
| 5670 | + |
| 5671 | + BUG_ON(plane == OMAP_DSS_GFX); |
| 5672 | + |
| 5673 | + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); |
| 5674 | + dispc_write_reg(vsi_reg[plane-1], val); |
| 5675 | +} |
| 5676 | + |
| 5677 | +static void _dispc_setup_global_alpha(enum omap_plane plane, u8 global_alpha) |
| 5678 | +{ |
| 5679 | + |
| 5680 | + BUG_ON(plane == OMAP_DSS_VIDEO1); |
| 5681 | + |
| 5682 | + if (plane == OMAP_DSS_GFX) |
| 5683 | + REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 7, 0); |
| 5684 | + else if (plane == OMAP_DSS_VIDEO2) |
| 5685 | + REG_FLD_MOD(DISPC_GLOBAL_ALPHA, global_alpha, 23, 16); |
| 5686 | +} |
| 5687 | + |
| 5688 | +static void _dispc_set_pix_inc(enum omap_plane plane, s32 inc) |
| 5689 | +{ |
| 5690 | + const struct dispc_reg ri_reg[] = { DISPC_GFX_PIXEL_INC, |
| 5691 | + DISPC_VID_PIXEL_INC(0), |
| 5692 | + DISPC_VID_PIXEL_INC(1) }; |
| 5693 | + |
| 5694 | + dispc_write_reg(ri_reg[plane], inc); |
| 5695 | +} |
| 5696 | + |
| 5697 | +static void _dispc_set_row_inc(enum omap_plane plane, s32 inc) |
| 5698 | +{ |
| 5699 | + const struct dispc_reg ri_reg[] = { DISPC_GFX_ROW_INC, |
| 5700 | + DISPC_VID_ROW_INC(0), |
| 5701 | + DISPC_VID_ROW_INC(1) }; |
| 5702 | + |
| 5703 | + dispc_write_reg(ri_reg[plane], inc); |
| 5704 | +} |
| 5705 | + |
| 5706 | +static void _dispc_set_color_mode(enum omap_plane plane, |
| 5707 | + enum omap_color_mode color_mode) |
| 5708 | +{ |
| 5709 | + u32 m = 0; |
| 5710 | + |
| 5711 | + switch (color_mode) { |
| 5712 | + case OMAP_DSS_COLOR_CLUT1: |
| 5713 | + m = 0x0; break; |
| 5714 | + case OMAP_DSS_COLOR_CLUT2: |
| 5715 | + m = 0x1; break; |
| 5716 | + case OMAP_DSS_COLOR_CLUT4: |
| 5717 | + m = 0x2; break; |
| 5718 | + case OMAP_DSS_COLOR_CLUT8: |
| 5719 | + m = 0x3; break; |
| 5720 | + case OMAP_DSS_COLOR_RGB12U: |
| 5721 | + m = 0x4; break; |
| 5722 | + case OMAP_DSS_COLOR_ARGB16: |
| 5723 | + m = 0x5; break; |
| 5724 | + case OMAP_DSS_COLOR_RGB16: |
| 5725 | + m = 0x6; break; |
| 5726 | + case OMAP_DSS_COLOR_RGB24U: |
| 5727 | + m = 0x8; break; |
| 5728 | + case OMAP_DSS_COLOR_RGB24P: |
| 5729 | + m = 0x9; break; |
| 5730 | + case OMAP_DSS_COLOR_YUV2: |
| 5731 | + m = 0xa; break; |
| 5732 | + case OMAP_DSS_COLOR_UYVY: |
| 5733 | + m = 0xb; break; |
| 5734 | + case OMAP_DSS_COLOR_ARGB32: |
| 5735 | + m = 0xc; break; |
| 5736 | + case OMAP_DSS_COLOR_RGBA32: |
| 5737 | + m = 0xd; break; |
| 5738 | + case OMAP_DSS_COLOR_RGBX32: |
| 5739 | + m = 0xe; break; |
| 5740 | + default: |
| 5741 | + BUG(); break; |
| 5742 | + } |
| 5743 | + |
| 5744 | + REG_FLD_MOD(dispc_reg_att[plane], m, 4, 1); |
| 5745 | +} |
| 5746 | + |
| 5747 | +static void _dispc_set_channel_out(enum omap_plane plane, |
| 5748 | + enum omap_channel channel) |
| 5749 | +{ |
| 5750 | + int shift; |
| 5751 | + u32 val; |
| 5752 | + |
| 5753 | + switch (plane) { |
| 5754 | + case OMAP_DSS_GFX: |
| 5755 | + shift = 8; |
| 5756 | + break; |
| 5757 | + case OMAP_DSS_VIDEO1: |
| 5758 | + case OMAP_DSS_VIDEO2: |
| 5759 | + shift = 16; |
| 5760 | + break; |
| 5761 | + default: |
| 5762 | + BUG(); |
| 5763 | + return; |
| 5764 | + } |
| 5765 | + |
| 5766 | + val = dispc_read_reg(dispc_reg_att[plane]); |
| 5767 | + val = FLD_MOD(val, channel, shift, shift); |
| 5768 | + dispc_write_reg(dispc_reg_att[plane], val); |
| 5769 | +} |
| 5770 | + |
| 5771 | +void dispc_set_burst_size(enum omap_plane plane, |
| 5772 | + enum omap_burst_size burst_size) |
| 5773 | +{ |
| 5774 | + int shift; |
| 5775 | + u32 val; |
| 5776 | + |
| 5777 | + enable_clocks(1); |
| 5778 | + |
| 5779 | + switch (plane) { |
| 5780 | + case OMAP_DSS_GFX: |
| 5781 | + shift = 6; |
| 5782 | + break; |
| 5783 | + case OMAP_DSS_VIDEO1: |
| 5784 | + case OMAP_DSS_VIDEO2: |
| 5785 | + shift = 14; |
| 5786 | + break; |
| 5787 | + default: |
| 5788 | + BUG(); |
| 5789 | + return; |
| 5790 | + } |
| 5791 | + |
| 5792 | + val = dispc_read_reg(dispc_reg_att[plane]); |
| 5793 | + val = FLD_MOD(val, burst_size, shift+1, shift); |
| 5794 | + dispc_write_reg(dispc_reg_att[plane], val); |
| 5795 | + |
| 5796 | + enable_clocks(0); |
| 5797 | +} |
| 5798 | + |
| 5799 | +static void _dispc_set_vid_color_conv(enum omap_plane plane, bool enable) |
| 5800 | +{ |
| 5801 | + u32 val; |
| 5802 | + |
| 5803 | + BUG_ON(plane == OMAP_DSS_GFX); |
| 5804 | + |
| 5805 | + val = dispc_read_reg(dispc_reg_att[plane]); |
| 5806 | + val = FLD_MOD(val, enable, 9, 9); |
| 5807 | + dispc_write_reg(dispc_reg_att[plane], val); |
| 5808 | +} |
| 5809 | + |
| 5810 | +void dispc_enable_replication(enum omap_plane plane, bool enable) |
| 5811 | +{ |
| 5812 | + int bit; |
| 5813 | + |
| 5814 | + if (plane == OMAP_DSS_GFX) |
| 5815 | + bit = 5; |
| 5816 | + else |
| 5817 | + bit = 10; |
| 5818 | + |
| 5819 | + enable_clocks(1); |
| 5820 | + REG_FLD_MOD(dispc_reg_att[plane], enable, bit, bit); |
| 5821 | + enable_clocks(0); |
| 5822 | +} |
| 5823 | + |
| 5824 | +void dispc_set_lcd_size(u16 width, u16 height) |
| 5825 | +{ |
| 5826 | + u32 val; |
| 5827 | + BUG_ON((width > (1 << 11)) || (height > (1 << 11))); |
| 5828 | + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); |
| 5829 | + enable_clocks(1); |
| 5830 | + dispc_write_reg(DISPC_SIZE_LCD, val); |
| 5831 | + enable_clocks(0); |
| 5832 | +} |
| 5833 | + |
| 5834 | +void dispc_set_digit_size(u16 width, u16 height) |
| 5835 | +{ |
| 5836 | + u32 val; |
| 5837 | + BUG_ON((width > (1 << 11)) || (height > (1 << 11))); |
| 5838 | + val = FLD_VAL(height - 1, 26, 16) | FLD_VAL(width - 1, 10, 0); |
| 5839 | + enable_clocks(1); |
| 5840 | + dispc_write_reg(DISPC_SIZE_DIG, val); |
| 5841 | + enable_clocks(0); |
| 5842 | +} |
| 5843 | + |
| 5844 | +static void dispc_read_plane_fifo_sizes(void) |
| 5845 | +{ |
| 5846 | + const struct dispc_reg fsz_reg[] = { DISPC_GFX_FIFO_SIZE_STATUS, |
| 5847 | + DISPC_VID_FIFO_SIZE_STATUS(0), |
| 5848 | + DISPC_VID_FIFO_SIZE_STATUS(1) }; |
| 5849 | + u32 size; |
| 5850 | + int plane; |
| 5851 | + |
| 5852 | + enable_clocks(1); |
| 5853 | + |
| 5854 | + for (plane = 0; plane < ARRAY_SIZE(dispc.fifo_size); ++plane) { |
| 5855 | + if (cpu_is_omap24xx()) |
| 5856 | + size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 8, 0); |
| 5857 | + else if (cpu_is_omap34xx()) |
| 5858 | + size = FLD_GET(dispc_read_reg(fsz_reg[plane]), 10, 0); |
| 5859 | + else |
| 5860 | + BUG(); |
| 5861 | + |
| 5862 | + dispc.fifo_size[plane] = size; |
| 5863 | + } |
| 5864 | + |
| 5865 | + enable_clocks(0); |
| 5866 | +} |
| 5867 | + |
| 5868 | +u32 dispc_get_plane_fifo_size(enum omap_plane plane) |
| 5869 | +{ |
| 5870 | + return dispc.fifo_size[plane]; |
| 5871 | +} |
| 5872 | + |
| 5873 | +void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high) |
| 5874 | +{ |
| 5875 | + const struct dispc_reg ftrs_reg[] = { DISPC_GFX_FIFO_THRESHOLD, |
| 5876 | + DISPC_VID_FIFO_THRESHOLD(0), |
| 5877 | + DISPC_VID_FIFO_THRESHOLD(1) }; |
| 5878 | + enable_clocks(1); |
| 5879 | + |
| 5880 | + DSSDBG("fifo(%d) low/high old %u/%u, new %u/%u\n", |
| 5881 | + plane, |
| 5882 | + REG_GET(ftrs_reg[plane], 11, 0), |
| 5883 | + REG_GET(ftrs_reg[plane], 27, 16), |
| 5884 | + low, high); |
| 5885 | + |
| 5886 | + if (cpu_is_omap24xx()) |
| 5887 | + dispc_write_reg(ftrs_reg[plane], |
| 5888 | + FLD_VAL(high, 24, 16) | FLD_VAL(low, 8, 0)); |
| 5889 | + else |
| 5890 | + dispc_write_reg(ftrs_reg[plane], |
| 5891 | + FLD_VAL(high, 27, 16) | FLD_VAL(low, 11, 0)); |
| 5892 | + |
| 5893 | + enable_clocks(0); |
| 5894 | +} |
| 5895 | + |
| 5896 | +void dispc_enable_fifomerge(bool enable) |
| 5897 | +{ |
| 5898 | + enable_clocks(1); |
| 5899 | + |
| 5900 | + DSSDBG("FIFO merge %s\n", enable ? "enabled" : "disabled"); |
| 5901 | + REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 14, 14); |
| 5902 | + |
| 5903 | + enable_clocks(0); |
| 5904 | +} |
| 5905 | + |
| 5906 | +static void _dispc_set_fir(enum omap_plane plane, int hinc, int vinc) |
| 5907 | +{ |
| 5908 | + u32 val; |
| 5909 | + const struct dispc_reg fir_reg[] = { DISPC_VID_FIR(0), |
| 5910 | + DISPC_VID_FIR(1) }; |
| 5911 | + |
| 5912 | + BUG_ON(plane == OMAP_DSS_GFX); |
| 5913 | + |
| 5914 | + if (cpu_is_omap24xx()) |
| 5915 | + val = FLD_VAL(vinc, 27, 16) | FLD_VAL(hinc, 11, 0); |
| 5916 | + else |
| 5917 | + val = FLD_VAL(vinc, 28, 16) | FLD_VAL(hinc, 12, 0); |
| 5918 | + dispc_write_reg(fir_reg[plane-1], val); |
| 5919 | +} |
| 5920 | + |
| 5921 | +static void _dispc_set_vid_accu0(enum omap_plane plane, int haccu, int vaccu) |
| 5922 | +{ |
| 5923 | + u32 val; |
| 5924 | + const struct dispc_reg ac0_reg[] = { DISPC_VID_ACCU0(0), |
| 5925 | + DISPC_VID_ACCU0(1) }; |
| 5926 | + |
| 5927 | + BUG_ON(plane == OMAP_DSS_GFX); |
| 5928 | + |
| 5929 | + val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); |
| 5930 | + dispc_write_reg(ac0_reg[plane-1], val); |
| 5931 | +} |
| 5932 | + |
| 5933 | +static void _dispc_set_vid_accu1(enum omap_plane plane, int haccu, int vaccu) |
| 5934 | +{ |
| 5935 | + u32 val; |
| 5936 | + const struct dispc_reg ac1_reg[] = { DISPC_VID_ACCU1(0), |
| 5937 | + DISPC_VID_ACCU1(1) }; |
| 5938 | + |
| 5939 | + BUG_ON(plane == OMAP_DSS_GFX); |
| 5940 | + |
| 5941 | + val = FLD_VAL(vaccu, 25, 16) | FLD_VAL(haccu, 9, 0); |
| 5942 | + dispc_write_reg(ac1_reg[plane-1], val); |
| 5943 | +} |
| 5944 | + |
| 5945 | + |
| 5946 | +static void _dispc_set_scaling(enum omap_plane plane, |
| 5947 | + u16 orig_width, u16 orig_height, |
| 5948 | + u16 out_width, u16 out_height, |
| 5949 | + bool ilace, bool five_taps, |
| 5950 | + bool fieldmode) |
| 5951 | +{ |
| 5952 | + int fir_hinc; |
| 5953 | + int fir_vinc; |
| 5954 | + int hscaleup, vscaleup; |
| 5955 | + int accu0 = 0; |
| 5956 | + int accu1 = 0; |
| 5957 | + u32 l; |
| 5958 | + |
| 5959 | + BUG_ON(plane == OMAP_DSS_GFX); |
| 5960 | + |
| 5961 | + hscaleup = orig_width <= out_width; |
| 5962 | + vscaleup = orig_height <= out_height; |
| 5963 | + |
| 5964 | + _dispc_set_scale_coef(plane, hscaleup, vscaleup, five_taps); |
| 5965 | + |
| 5966 | + if (!orig_width || orig_width == out_width) |
| 5967 | + fir_hinc = 0; |
| 5968 | + else |
| 5969 | + fir_hinc = 1024 * orig_width / out_width; |
| 5970 | + |
| 5971 | + if (!orig_height || orig_height == out_height) |
| 5972 | + fir_vinc = 0; |
| 5973 | + else |
| 5974 | + fir_vinc = 1024 * orig_height / out_height; |
| 5975 | + |
| 5976 | + _dispc_set_fir(plane, fir_hinc, fir_vinc); |
| 5977 | + |
| 5978 | + l = dispc_read_reg(dispc_reg_att[plane]); |
| 5979 | + l &= ~((0x0f << 5) | (0x3 << 21)); |
| 5980 | + |
| 5981 | + l |= fir_hinc ? (1 << 5) : 0; |
| 5982 | + l |= fir_vinc ? (1 << 6) : 0; |
| 5983 | + |
| 5984 | + l |= hscaleup ? 0 : (1 << 7); |
| 5985 | + l |= vscaleup ? 0 : (1 << 8); |
| 5986 | + |
| 5987 | + l |= five_taps ? (1 << 21) : 0; |
| 5988 | + l |= five_taps ? (1 << 22) : 0; |
| 5989 | + |
| 5990 | + dispc_write_reg(dispc_reg_att[plane], l); |
| 5991 | + |
| 5992 | + /* |
| 5993 | + * field 0 = even field = bottom field |
| 5994 | + * field 1 = odd field = top field |
| 5995 | + */ |
| 5996 | + if (ilace && !fieldmode) { |
| 5997 | + accu1 = 0; |
| 5998 | + accu0 = (fir_vinc / 2) & 0x3ff; |
| 5999 | + if (accu0 >= 1024/2) { |
| 6000 | + accu1 = 1024/2; |
| 6001 | + accu0 -= accu1; |
| 6002 | + } |
| 6003 | + } |
| 6004 | + |
| 6005 | + _dispc_set_vid_accu0(plane, 0, accu0); |
| 6006 | + _dispc_set_vid_accu1(plane, 0, accu1); |
| 6007 | +} |
| 6008 | + |
| 6009 | +static void _dispc_set_rotation_attrs(enum omap_plane plane, u8 rotation, |
| 6010 | + bool mirroring, enum omap_color_mode color_mode) |
| 6011 | +{ |
| 6012 | + if (color_mode == OMAP_DSS_COLOR_YUV2 || |
| 6013 | + color_mode == OMAP_DSS_COLOR_UYVY) { |
| 6014 | + int vidrot = 0; |
| 6015 | + |
| 6016 | + if (mirroring) { |
| 6017 | + switch (rotation) { |
| 6018 | + case 0: |
| 6019 | + vidrot = 2; |
| 6020 | + break; |
| 6021 | + case 1: |
| 6022 | + vidrot = 1; |
| 6023 | + break; |
| 6024 | + case 2: |
| 6025 | + vidrot = 0; |
| 6026 | + break; |
| 6027 | + case 3: |
| 6028 | + vidrot = 3; |
| 6029 | + break; |
| 6030 | + } |
| 6031 | + } else { |
| 6032 | + switch (rotation) { |
| 6033 | + case 0: |
| 6034 | + vidrot = 0; |
| 6035 | + break; |
| 6036 | + case 1: |
| 6037 | + vidrot = 1; |
| 6038 | + break; |
| 6039 | + case 2: |
| 6040 | + vidrot = 2; |
| 6041 | + break; |
| 6042 | + case 3: |
| 6043 | + vidrot = 3; |
| 6044 | + break; |
| 6045 | + } |
| 6046 | + } |
| 6047 | + |
| 6048 | + REG_FLD_MOD(dispc_reg_att[plane], vidrot, 13, 12); |
| 6049 | + |
| 6050 | + if (rotation == 1 || rotation == 3) |
| 6051 | + REG_FLD_MOD(dispc_reg_att[plane], 0x1, 18, 18); |
| 6052 | + else |
| 6053 | + REG_FLD_MOD(dispc_reg_att[plane], 0x0, 18, 18); |
| 6054 | + } else { |
| 6055 | + REG_FLD_MOD(dispc_reg_att[plane], 0, 13, 12); |
| 6056 | + REG_FLD_MOD(dispc_reg_att[plane], 0, 18, 18); |
| 6057 | + } |
| 6058 | +} |
| 6059 | + |
| 6060 | +static s32 pixinc(int pixels, u8 ps) |
| 6061 | +{ |
| 6062 | + if (pixels == 1) |
| 6063 | + return 1; |
| 6064 | + else if (pixels > 1) |
| 6065 | + return 1 + (pixels - 1) * ps; |
| 6066 | + else if (pixels < 0) |
| 6067 | + return 1 - (-pixels + 1) * ps; |
| 6068 | + else |
| 6069 | + BUG(); |
| 6070 | +} |
| 6071 | + |
| 6072 | +static void calc_vrfb_rotation_offset(u8 rotation, bool mirror, |
| 6073 | + u16 screen_width, |
| 6074 | + u16 width, u16 height, |
| 6075 | + enum omap_color_mode color_mode, bool fieldmode, |
| 6076 | + unsigned int field_offset, |
| 6077 | + unsigned *offset0, unsigned *offset1, |
| 6078 | + s32 *row_inc, s32 *pix_inc) |
| 6079 | +{ |
| 6080 | + u8 ps; |
| 6081 | + |
| 6082 | + switch (color_mode) { |
| 6083 | + case OMAP_DSS_COLOR_RGB16: |
| 6084 | + case OMAP_DSS_COLOR_ARGB16: |
| 6085 | + ps = 2; |
| 6086 | + break; |
| 6087 | + |
| 6088 | + case OMAP_DSS_COLOR_RGB24P: |
| 6089 | + ps = 3; |
| 6090 | + break; |
| 6091 | + |
| 6092 | + case OMAP_DSS_COLOR_RGB24U: |
| 6093 | + case OMAP_DSS_COLOR_ARGB32: |
| 6094 | + case OMAP_DSS_COLOR_RGBA32: |
| 6095 | + case OMAP_DSS_COLOR_RGBX32: |
| 6096 | + case OMAP_DSS_COLOR_YUV2: |
| 6097 | + case OMAP_DSS_COLOR_UYVY: |
| 6098 | + ps = 4; |
| 6099 | + break; |
| 6100 | + |
| 6101 | + default: |
| 6102 | + BUG(); |
| 6103 | + return; |
| 6104 | + } |
| 6105 | + |
| 6106 | + DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width, |
| 6107 | + width, height); |
| 6108 | + |
| 6109 | + /* |
| 6110 | + * field 0 = even field = bottom field |
| 6111 | + * field 1 = odd field = top field |
| 6112 | + */ |
| 6113 | + switch (rotation + mirror * 4) { |
| 6114 | + case 0: |
| 6115 | + case 2: |
| 6116 | + /* |
| 6117 | + * If the pixel format is YUV or UYVY divide the width |
| 6118 | + * of the image by 2 for 0 and 180 degree rotation. |
| 6119 | + */ |
| 6120 | + if (color_mode == OMAP_DSS_COLOR_YUV2 || |
| 6121 | + color_mode == OMAP_DSS_COLOR_UYVY) |
| 6122 | + width = width >> 1; |
| 6123 | + case 1: |
| 6124 | + case 3: |
| 6125 | + *offset1 = 0; |
| 6126 | + if (field_offset) |
| 6127 | + *offset0 = field_offset * screen_width * ps; |
| 6128 | + else |
| 6129 | + *offset0 = 0; |
| 6130 | + |
| 6131 | + *row_inc = pixinc(1 + (screen_width - width) + |
| 6132 | + (fieldmode ? screen_width : 0), |
| 6133 | + ps); |
| 6134 | + *pix_inc = pixinc(1, ps); |
| 6135 | + break; |
| 6136 | + |
| 6137 | + case 4: |
| 6138 | + case 6: |
| 6139 | + /* If the pixel format is YUV or UYVY divide the width |
| 6140 | + * of the image by 2 for 0 degree and 180 degree |
| 6141 | + */ |
| 6142 | + if (color_mode == OMAP_DSS_COLOR_YUV2 || |
| 6143 | + color_mode == OMAP_DSS_COLOR_UYVY) |
| 6144 | + width = width >> 1; |
| 6145 | + case 5: |
| 6146 | + case 7: |
| 6147 | + *offset1 = 0; |
| 6148 | + if (field_offset) |
| 6149 | + *offset0 = field_offset * screen_width * ps; |
| 6150 | + else |
| 6151 | + *offset0 = 0; |
| 6152 | + *row_inc = pixinc(1 - (screen_width + width) - |
| 6153 | + (fieldmode ? screen_width : 0), |
| 6154 | + ps); |
| 6155 | + *pix_inc = pixinc(1, ps); |
| 6156 | + break; |
| 6157 | + |
| 6158 | + default: |
| 6159 | + BUG(); |
| 6160 | + } |
| 6161 | +} |
| 6162 | + |
| 6163 | +static void calc_dma_rotation_offset(u8 rotation, bool mirror, |
| 6164 | + u16 screen_width, |
| 6165 | + u16 width, u16 height, |
| 6166 | + enum omap_color_mode color_mode, bool fieldmode, |
| 6167 | + unsigned int field_offset, |
| 6168 | + unsigned *offset0, unsigned *offset1, |
| 6169 | + s32 *row_inc, s32 *pix_inc) |
| 6170 | +{ |
| 6171 | + u8 ps; |
| 6172 | + u16 fbw, fbh; |
| 6173 | + |
| 6174 | + switch (color_mode) { |
| 6175 | + case OMAP_DSS_COLOR_RGB16: |
| 6176 | + case OMAP_DSS_COLOR_ARGB16: |
| 6177 | + ps = 2; |
| 6178 | + break; |
| 6179 | + |
| 6180 | + case OMAP_DSS_COLOR_RGB24P: |
| 6181 | + ps = 3; |
| 6182 | + break; |
| 6183 | + |
| 6184 | + case OMAP_DSS_COLOR_RGB24U: |
| 6185 | + case OMAP_DSS_COLOR_ARGB32: |
| 6186 | + case OMAP_DSS_COLOR_RGBA32: |
| 6187 | + case OMAP_DSS_COLOR_RGBX32: |
| 6188 | + ps = 4; |
| 6189 | + break; |
| 6190 | + |
| 6191 | + case OMAP_DSS_COLOR_YUV2: |
| 6192 | + case OMAP_DSS_COLOR_UYVY: |
| 6193 | + ps = 2; |
| 6194 | + break; |
| 6195 | + default: |
| 6196 | + BUG(); |
| 6197 | + return; |
| 6198 | + } |
| 6199 | + |
| 6200 | + DSSDBG("calc_rot(%d): scrw %d, %dx%d\n", rotation, screen_width, |
| 6201 | + width, height); |
| 6202 | + |
| 6203 | + /* width & height are overlay sizes, convert to fb sizes */ |
| 6204 | + |
| 6205 | + if (rotation == 0 || rotation == 2) { |
| 6206 | + fbw = width; |
| 6207 | + fbh = height; |
| 6208 | + } else { |
| 6209 | + fbw = height; |
| 6210 | + fbh = width; |
| 6211 | + } |
| 6212 | + |
| 6213 | + /* |
| 6214 | + * field 0 = even field = bottom field |
| 6215 | + * field 1 = odd field = top field |
| 6216 | + */ |
| 6217 | + switch (rotation + mirror * 4) { |
| 6218 | + case 0: |
| 6219 | + *offset1 = 0; |
| 6220 | + if (field_offset) |
| 6221 | + *offset0 = *offset1 + field_offset * screen_width * ps; |
| 6222 | + else |
| 6223 | + *offset0 = *offset1; |
| 6224 | + *row_inc = pixinc(1 + (screen_width - fbw) + |
| 6225 | + (fieldmode ? screen_width : 0), |
| 6226 | + ps); |
| 6227 | + *pix_inc = pixinc(1, ps); |
| 6228 | + break; |
| 6229 | + case 1: |
| 6230 | + *offset1 = screen_width * (fbh - 1) * ps; |
| 6231 | + if (field_offset) |
| 6232 | + *offset0 = *offset1 + field_offset * ps; |
| 6233 | + else |
| 6234 | + *offset0 = *offset1; |
| 6235 | + *row_inc = pixinc(screen_width * (fbh - 1) + 1 + |
| 6236 | + (fieldmode ? 1 : 0), ps); |
| 6237 | + *pix_inc = pixinc(-screen_width, ps); |
| 6238 | + break; |
| 6239 | + case 2: |
| 6240 | + *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; |
| 6241 | + if (field_offset) |
| 6242 | + *offset0 = *offset1 - field_offset * screen_width * ps; |
| 6243 | + else |
| 6244 | + *offset0 = *offset1; |
| 6245 | + *row_inc = pixinc(-1 - |
| 6246 | + (screen_width - fbw) - |
| 6247 | + (fieldmode ? screen_width : 0), |
| 6248 | + ps); |
| 6249 | + *pix_inc = pixinc(-1, ps); |
| 6250 | + break; |
| 6251 | + case 3: |
| 6252 | + *offset1 = (fbw - 1) * ps; |
| 6253 | + if (field_offset) |
| 6254 | + *offset0 = *offset1 - field_offset * ps; |
| 6255 | + else |
| 6256 | + *offset0 = *offset1; |
| 6257 | + *row_inc = pixinc(-screen_width * (fbh - 1) - 1 - |
| 6258 | + (fieldmode ? 1 : 0), ps); |
| 6259 | + *pix_inc = pixinc(screen_width, ps); |
| 6260 | + break; |
| 6261 | + |
| 6262 | + /* mirroring */ |
| 6263 | + case 0 + 4: |
| 6264 | + *offset1 = (fbw - 1) * ps; |
| 6265 | + if (field_offset) |
| 6266 | + *offset0 = *offset1 + field_offset * screen_width * ps; |
| 6267 | + else |
| 6268 | + *offset0 = *offset1; |
| 6269 | + *row_inc = pixinc(screen_width * 2 - 1 + |
| 6270 | + (fieldmode ? screen_width : 0), |
| 6271 | + ps); |
| 6272 | + *pix_inc = pixinc(-1, ps); |
| 6273 | + break; |
| 6274 | + |
| 6275 | + case 1 + 4: |
| 6276 | + *offset1 = 0; |
| 6277 | + if (field_offset) |
| 6278 | + *offset0 = *offset1 + field_offset * ps; |
| 6279 | + else |
| 6280 | + *offset0 = *offset1; |
| 6281 | + *row_inc = pixinc(-screen_width * (fbh - 1) + 1 + |
| 6282 | + (fieldmode ? 1 : 0), |
| 6283 | + ps); |
| 6284 | + *pix_inc = pixinc(screen_width, ps); |
| 6285 | + break; |
| 6286 | + |
| 6287 | + case 2 + 4: |
| 6288 | + *offset1 = screen_width * (fbh - 1) * ps; |
| 6289 | + if (field_offset) |
| 6290 | + *offset0 = *offset1 - field_offset * screen_width * ps; |
| 6291 | + else |
| 6292 | + *offset0 = *offset1; |
| 6293 | + *row_inc = pixinc(1 - screen_width * 2 - |
| 6294 | + (fieldmode ? screen_width : 0), |
| 6295 | + ps); |
| 6296 | + *pix_inc = pixinc(1, ps); |
| 6297 | + break; |
| 6298 | + |
| 6299 | + case 3 + 4: |
| 6300 | + *offset1 = (screen_width * (fbh - 1) + fbw - 1) * ps; |
| 6301 | + if (field_offset) |
| 6302 | + *offset0 = *offset1 - field_offset * ps; |
| 6303 | + else |
| 6304 | + *offset0 = *offset1; |
| 6305 | + *row_inc = pixinc(screen_width * (fbh - 1) - 1 - |
| 6306 | + (fieldmode ? 1 : 0), |
| 6307 | + ps); |
| 6308 | + *pix_inc = pixinc(-screen_width, ps); |
| 6309 | + break; |
| 6310 | + |
| 6311 | + default: |
| 6312 | + BUG(); |
| 6313 | + } |
| 6314 | +} |
| 6315 | + |
| 6316 | +static unsigned long calc_fclk_five_taps(u16 width, u16 height, |
| 6317 | + u16 out_width, u16 out_height, enum omap_color_mode color_mode) |
| 6318 | +{ |
| 6319 | + u32 fclk = 0; |
| 6320 | + /* FIXME venc pclk? */ |
| 6321 | + u64 tmp, pclk = dispc_pclk_rate(); |
| 6322 | + |
| 6323 | + if (height > out_height) { |
| 6324 | + /* FIXME get real display PPL */ |
| 6325 | + unsigned int ppl = 800; |
| 6326 | + |
| 6327 | + tmp = pclk * height * out_width; |
| 6328 | + do_div(tmp, 2 * out_height * ppl); |
| 6329 | + fclk = tmp; |
| 6330 | + |
| 6331 | + if (height > 2 * out_height && ppl != out_width) { |
| 6332 | + tmp = pclk * (height - 2 * out_height) * out_width; |
| 6333 | + do_div(tmp, 2 * out_height * (ppl - out_width)); |
| 6334 | + fclk = max(fclk, (u32) tmp); |
| 6335 | + } |
| 6336 | + } |
| 6337 | + |
| 6338 | + if (width > out_width) { |
| 6339 | + tmp = pclk * width; |
| 6340 | + do_div(tmp, out_width); |
| 6341 | + fclk = max(fclk, (u32) tmp); |
| 6342 | + |
| 6343 | + if (color_mode == OMAP_DSS_COLOR_RGB24U) |
| 6344 | + fclk <<= 1; |
| 6345 | + } |
| 6346 | + |
| 6347 | + return fclk; |
| 6348 | +} |
| 6349 | + |
| 6350 | +static unsigned long calc_fclk(u16 width, u16 height, |
| 6351 | + u16 out_width, u16 out_height) |
| 6352 | +{ |
| 6353 | + unsigned int hf, vf; |
| 6354 | + |
| 6355 | + /* |
| 6356 | + * FIXME how to determine the 'A' factor |
| 6357 | + * for the no downscaling case ? |
| 6358 | + */ |
| 6359 | + |
| 6360 | + if (width > 3 * out_width) |
| 6361 | + hf = 4; |
| 6362 | + else if (width > 2 * out_width) |
| 6363 | + hf = 3; |
| 6364 | + else if (width > out_width) |
| 6365 | + hf = 2; |
| 6366 | + else |
| 6367 | + hf = 1; |
| 6368 | + |
| 6369 | + if (height > out_height) |
| 6370 | + vf = 2; |
| 6371 | + else |
| 6372 | + vf = 1; |
| 6373 | + |
| 6374 | + /* FIXME venc pclk? */ |
| 6375 | + return dispc_pclk_rate() * vf * hf; |
| 6376 | +} |
| 6377 | + |
| 6378 | +void dispc_set_channel_out(enum omap_plane plane, enum omap_channel channel_out) |
| 6379 | +{ |
| 6380 | + enable_clocks(1); |
| 6381 | + _dispc_set_channel_out(plane, channel_out); |
| 6382 | + enable_clocks(0); |
| 6383 | +} |
| 6384 | + |
| 6385 | +static int _dispc_setup_plane(enum omap_plane plane, |
| 6386 | + u32 paddr, u16 screen_width, |
| 6387 | + u16 pos_x, u16 pos_y, |
| 6388 | + u16 width, u16 height, |
| 6389 | + u16 out_width, u16 out_height, |
| 6390 | + enum omap_color_mode color_mode, |
| 6391 | + bool ilace, |
| 6392 | + enum omap_dss_rotation_type rotation_type, |
| 6393 | + u8 rotation, int mirror, |
| 6394 | + u8 global_alpha) |
| 6395 | +{ |
| 6396 | + const int maxdownscale = cpu_is_omap34xx() ? 4 : 2; |
| 6397 | + bool five_taps = 0; |
| 6398 | + bool fieldmode = 0; |
| 6399 | + int cconv = 0; |
| 6400 | + unsigned offset0, offset1; |
| 6401 | + s32 row_inc; |
| 6402 | + s32 pix_inc; |
| 6403 | + u16 frame_height = height; |
| 6404 | + unsigned int field_offset = 0; |
| 6405 | + |
| 6406 | + if (paddr == 0) |
| 6407 | + return -EINVAL; |
| 6408 | + |
| 6409 | + if (ilace && height == out_height) |
| 6410 | + fieldmode = 1; |
| 6411 | + |
| 6412 | + if (ilace) { |
| 6413 | + if (fieldmode) |
| 6414 | + height /= 2; |
| 6415 | + pos_y /= 2; |
| 6416 | + out_height /= 2; |
| 6417 | + |
| 6418 | + DSSDBG("adjusting for ilace: height %d, pos_y %d, " |
| 6419 | + "out_height %d\n", |
| 6420 | + height, pos_y, out_height); |
| 6421 | + } |
| 6422 | + |
| 6423 | + if (plane == OMAP_DSS_GFX) { |
| 6424 | + if (width != out_width || height != out_height) |
| 6425 | + return -EINVAL; |
| 6426 | + |
| 6427 | + switch (color_mode) { |
| 6428 | + case OMAP_DSS_COLOR_ARGB16: |
| 6429 | + case OMAP_DSS_COLOR_RGB16: |
| 6430 | + case OMAP_DSS_COLOR_RGB24P: |
| 6431 | + case OMAP_DSS_COLOR_RGB24U: |
| 6432 | + case OMAP_DSS_COLOR_ARGB32: |
| 6433 | + case OMAP_DSS_COLOR_RGBA32: |
| 6434 | + case OMAP_DSS_COLOR_RGBX32: |
| 6435 | + break; |
| 6436 | + |
| 6437 | + default: |
| 6438 | + return -EINVAL; |
| 6439 | + } |
| 6440 | + } else { |
| 6441 | + /* video plane */ |
| 6442 | + |
| 6443 | + unsigned long fclk = 0; |
| 6444 | + |
| 6445 | + if (out_width < width / maxdownscale || |
| 6446 | + out_width > width * 8) |
| 6447 | + return -EINVAL; |
| 6448 | + |
| 6449 | + if (out_height < height / maxdownscale || |
| 6450 | + out_height > height * 8) |
| 6451 | + return -EINVAL; |
| 6452 | + |
| 6453 | + switch (color_mode) { |
| 6454 | + case OMAP_DSS_COLOR_RGB16: |
| 6455 | + case OMAP_DSS_COLOR_RGB24P: |
| 6456 | + case OMAP_DSS_COLOR_RGB24U: |
| 6457 | + case OMAP_DSS_COLOR_RGBX32: |
| 6458 | + break; |
| 6459 | + |
| 6460 | + case OMAP_DSS_COLOR_ARGB16: |
| 6461 | + case OMAP_DSS_COLOR_ARGB32: |
| 6462 | + case OMAP_DSS_COLOR_RGBA32: |
| 6463 | + if (plane == OMAP_DSS_VIDEO1) |
| 6464 | + return -EINVAL; |
| 6465 | + break; |
| 6466 | + |
| 6467 | + case OMAP_DSS_COLOR_YUV2: |
| 6468 | + case OMAP_DSS_COLOR_UYVY: |
| 6469 | + cconv = 1; |
| 6470 | + break; |
| 6471 | + |
| 6472 | + default: |
| 6473 | + return -EINVAL; |
| 6474 | + } |
| 6475 | + |
| 6476 | + /* Must use 5-tap filter? */ |
| 6477 | + five_taps = height > out_height * 2; |
| 6478 | + |
| 6479 | + if (!five_taps) { |
| 6480 | + fclk = calc_fclk(width, height, |
| 6481 | + out_width, out_height); |
| 6482 | + |
| 6483 | + /* Try 5-tap filter if 3-tap fclk is too high */ |
| 6484 | + if (cpu_is_omap34xx() && height > out_height && |
| 6485 | + fclk > dispc_fclk_rate()) |
| 6486 | + five_taps = true; |
| 6487 | + } |
| 6488 | + |
| 6489 | + if (width > (2048 >> five_taps)) |
| 6490 | + return -EINVAL; |
| 6491 | + |
| 6492 | + if (five_taps) |
| 6493 | + fclk = calc_fclk_five_taps(width, height, |
| 6494 | + out_width, out_height, color_mode); |
| 6495 | + |
| 6496 | + DSSDBG("required fclk rate = %lu Hz\n", fclk); |
| 6497 | + DSSDBG("current fclk rate = %lu Hz\n", dispc_fclk_rate()); |
| 6498 | + |
| 6499 | + if (fclk > dispc_fclk_rate()) { |
| 6500 | + DSSERR("failed to set up scaling, " |
| 6501 | + "required fclk rate = %lu Hz, " |
| 6502 | + "current fclk rate = %lu Hz\n", |
| 6503 | + fclk, dispc_fclk_rate()); |
| 6504 | + return -EINVAL; |
| 6505 | + } |
| 6506 | + } |
| 6507 | + |
| 6508 | + if (ilace && !fieldmode) { |
| 6509 | + /* |
| 6510 | + * when downscaling the bottom field may have to start several |
| 6511 | + * source lines below the top field. Unfortunately ACCUI |
| 6512 | + * registers will only hold the fractional part of the offset |
| 6513 | + * so the integer part must be added to the base address of the |
| 6514 | + * bottom field. |
| 6515 | + */ |
| 6516 | + if (!height || height == out_height) |
| 6517 | + field_offset = 0; |
| 6518 | + else |
| 6519 | + field_offset = height / out_height / 2; |
| 6520 | + } |
| 6521 | + |
| 6522 | + /* Fields are independent but interleaved in memory. */ |
| 6523 | + if (fieldmode) |
| 6524 | + field_offset = 1; |
| 6525 | + |
| 6526 | + if (rotation_type == OMAP_DSS_ROT_DMA) |
| 6527 | + calc_dma_rotation_offset(rotation, mirror, |
| 6528 | + screen_width, width, frame_height, color_mode, |
| 6529 | + fieldmode, field_offset, |
| 6530 | + &offset0, &offset1, &row_inc, &pix_inc); |
| 6531 | + else |
| 6532 | + calc_vrfb_rotation_offset(rotation, mirror, |
| 6533 | + screen_width, width, frame_height, color_mode, |
| 6534 | + fieldmode, field_offset, |
| 6535 | + &offset0, &offset1, &row_inc, &pix_inc); |
| 6536 | + |
| 6537 | + DSSDBG("offset0 %u, offset1 %u, row_inc %d, pix_inc %d\n", |
| 6538 | + offset0, offset1, row_inc, pix_inc); |
| 6539 | + |
| 6540 | + _dispc_set_color_mode(plane, color_mode); |
| 6541 | + |
| 6542 | + _dispc_set_plane_ba0(plane, paddr + offset0); |
| 6543 | + _dispc_set_plane_ba1(plane, paddr + offset1); |
| 6544 | + |
| 6545 | + _dispc_set_row_inc(plane, row_inc); |
| 6546 | + _dispc_set_pix_inc(plane, pix_inc); |
| 6547 | + |
| 6548 | + DSSDBG("%d,%d %dx%d -> %dx%d\n", pos_x, pos_y, width, height, |
| 6549 | + out_width, out_height); |
| 6550 | + |
| 6551 | + _dispc_set_plane_pos(plane, pos_x, pos_y); |
| 6552 | + |
| 6553 | + _dispc_set_pic_size(plane, width, height); |
| 6554 | + |
| 6555 | + if (plane != OMAP_DSS_GFX) { |
| 6556 | + _dispc_set_scaling(plane, width, height, |
| 6557 | + out_width, out_height, |
| 6558 | + ilace, five_taps, fieldmode); |
| 6559 | + _dispc_set_vid_size(plane, out_width, out_height); |
| 6560 | + _dispc_set_vid_color_conv(plane, cconv); |
| 6561 | + } |
| 6562 | + |
| 6563 | + _dispc_set_rotation_attrs(plane, rotation, mirror, color_mode); |
| 6564 | + |
| 6565 | + if (plane != OMAP_DSS_VIDEO1) |
| 6566 | + _dispc_setup_global_alpha(plane, global_alpha); |
| 6567 | + |
| 6568 | + return 0; |
| 6569 | +} |
| 6570 | + |
| 6571 | +static void _dispc_enable_plane(enum omap_plane plane, bool enable) |
| 6572 | +{ |
| 6573 | + REG_FLD_MOD(dispc_reg_att[plane], enable ? 1 : 0, 0, 0); |
| 6574 | +} |
| 6575 | + |
| 6576 | +static void dispc_disable_isr(void *data, u32 mask) |
| 6577 | +{ |
| 6578 | + struct completion *compl = data; |
| 6579 | + complete(compl); |
| 6580 | +} |
| 6581 | + |
| 6582 | +static void _enable_lcd_out(bool enable) |
| 6583 | +{ |
| 6584 | + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 0, 0); |
| 6585 | +} |
| 6586 | + |
| 6587 | +void dispc_enable_lcd_out(bool enable) |
| 6588 | +{ |
| 6589 | + struct completion frame_done_completion; |
| 6590 | + bool is_on; |
| 6591 | + int r; |
| 6592 | + |
| 6593 | + enable_clocks(1); |
| 6594 | + |
| 6595 | + /* When we disable LCD output, we need to wait until frame is done. |
| 6596 | + * Otherwise the DSS is still working, and turning off the clocks |
| 6597 | + * prevents DSS from going to OFF mode */ |
| 6598 | + is_on = REG_GET(DISPC_CONTROL, 0, 0); |
| 6599 | + |
| 6600 | + if (!enable && is_on) { |
| 6601 | + init_completion(&frame_done_completion); |
| 6602 | + |
| 6603 | + r = omap_dispc_register_isr(dispc_disable_isr, |
| 6604 | + &frame_done_completion, |
| 6605 | + DISPC_IRQ_FRAMEDONE); |
| 6606 | + |
| 6607 | + if (r) |
| 6608 | + DSSERR("failed to register FRAMEDONE isr\n"); |
| 6609 | + } |
| 6610 | + |
| 6611 | + _enable_lcd_out(enable); |
| 6612 | + |
| 6613 | + if (!enable && is_on) { |
| 6614 | + if (!wait_for_completion_timeout(&frame_done_completion, |
| 6615 | + msecs_to_jiffies(100))) |
| 6616 | + DSSERR("timeout waiting for FRAME DONE\n"); |
| 6617 | + |
| 6618 | + r = omap_dispc_unregister_isr(dispc_disable_isr, |
| 6619 | + &frame_done_completion, |
| 6620 | + DISPC_IRQ_FRAMEDONE); |
| 6621 | + |
| 6622 | + if (r) |
| 6623 | + DSSERR("failed to unregister FRAMEDONE isr\n"); |
| 6624 | + } |
| 6625 | + |
| 6626 | + enable_clocks(0); |
| 6627 | +} |
| 6628 | + |
| 6629 | +static void _enable_digit_out(bool enable) |
| 6630 | +{ |
| 6631 | + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 1, 1); |
| 6632 | +} |
| 6633 | + |
| 6634 | +void dispc_enable_digit_out(bool enable) |
| 6635 | +{ |
| 6636 | + struct completion frame_done_completion; |
| 6637 | + int r; |
| 6638 | + |
| 6639 | + enable_clocks(1); |
| 6640 | + |
| 6641 | + if (REG_GET(DISPC_CONTROL, 1, 1) == enable) { |
| 6642 | + enable_clocks(0); |
| 6643 | + return; |
| 6644 | + } |
| 6645 | + |
| 6646 | + if (enable) { |
| 6647 | + unsigned long flags; |
| 6648 | + /* When we enable digit output, we'll get an extra digit |
| 6649 | + * sync lost interrupt, that we need to ignore */ |
| 6650 | + spin_lock_irqsave(&dispc.irq_lock, flags); |
| 6651 | + dispc.irq_error_mask &= ~DISPC_IRQ_SYNC_LOST_DIGIT; |
| 6652 | + _omap_dispc_set_irqs(); |
| 6653 | + spin_unlock_irqrestore(&dispc.irq_lock, flags); |
| 6654 | + } |
| 6655 | + |
| 6656 | + /* When we disable digit output, we need to wait until fields are done. |
| 6657 | + * Otherwise the DSS is still working, and turning off the clocks |
| 6658 | + * prevents DSS from going to OFF mode. And when enabling, we need to |
| 6659 | + * wait for the extra sync losts */ |
| 6660 | + init_completion(&frame_done_completion); |
| 6661 | + |
| 6662 | + r = omap_dispc_register_isr(dispc_disable_isr, &frame_done_completion, |
| 6663 | + DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD); |
| 6664 | + if (r) |
| 6665 | + DSSERR("failed to register EVSYNC isr\n"); |
| 6666 | + |
| 6667 | + _enable_digit_out(enable); |
| 6668 | + |
| 6669 | + /* XXX I understand from TRM that we should only wait for the |
| 6670 | + * current field to complete. But it seems we have to wait |
| 6671 | + * for both fields */ |
| 6672 | + if (!wait_for_completion_timeout(&frame_done_completion, |
| 6673 | + msecs_to_jiffies(100))) |
| 6674 | + DSSERR("timeout waiting for EVSYNC\n"); |
| 6675 | + |
| 6676 | + if (!wait_for_completion_timeout(&frame_done_completion, |
| 6677 | + msecs_to_jiffies(100))) |
| 6678 | + DSSERR("timeout waiting for EVSYNC\n"); |
| 6679 | + |
| 6680 | + r = omap_dispc_unregister_isr(dispc_disable_isr, |
| 6681 | + &frame_done_completion, |
| 6682 | + DISPC_IRQ_EVSYNC_EVEN | DISPC_IRQ_EVSYNC_ODD); |
| 6683 | + if (r) |
| 6684 | + DSSERR("failed to unregister EVSYNC isr\n"); |
| 6685 | + |
| 6686 | + if (enable) { |
| 6687 | + unsigned long flags; |
| 6688 | + spin_lock_irqsave(&dispc.irq_lock, flags); |
| 6689 | + dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR; |
| 6690 | + dispc_write_reg(DISPC_IRQSTATUS, DISPC_IRQ_SYNC_LOST_DIGIT); |
| 6691 | + _omap_dispc_set_irqs(); |
| 6692 | + spin_unlock_irqrestore(&dispc.irq_lock, flags); |
| 6693 | + } |
| 6694 | + |
| 6695 | + enable_clocks(0); |
| 6696 | +} |
| 6697 | + |
| 6698 | +void dispc_lcd_enable_signal_polarity(bool act_high) |
| 6699 | +{ |
| 6700 | + enable_clocks(1); |
| 6701 | + REG_FLD_MOD(DISPC_CONTROL, act_high ? 1 : 0, 29, 29); |
| 6702 | + enable_clocks(0); |
| 6703 | +} |
| 6704 | + |
| 6705 | +void dispc_lcd_enable_signal(bool enable) |
| 6706 | +{ |
| 6707 | + enable_clocks(1); |
| 6708 | + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 28, 28); |
| 6709 | + enable_clocks(0); |
| 6710 | +} |
| 6711 | + |
| 6712 | +void dispc_pck_free_enable(bool enable) |
| 6713 | +{ |
| 6714 | + enable_clocks(1); |
| 6715 | + REG_FLD_MOD(DISPC_CONTROL, enable ? 1 : 0, 27, 27); |
| 6716 | + enable_clocks(0); |
| 6717 | +} |
| 6718 | + |
| 6719 | +void dispc_enable_fifohandcheck(bool enable) |
| 6720 | +{ |
| 6721 | + enable_clocks(1); |
| 6722 | + REG_FLD_MOD(DISPC_CONFIG, enable ? 1 : 0, 16, 16); |
| 6723 | + enable_clocks(0); |
| 6724 | +} |
| 6725 | + |
| 6726 | + |
| 6727 | +void dispc_set_lcd_display_type(enum omap_lcd_display_type type) |
| 6728 | +{ |
| 6729 | + int mode; |
| 6730 | + |
| 6731 | + switch (type) { |
| 6732 | + case OMAP_DSS_LCD_DISPLAY_STN: |
| 6733 | + mode = 0; |
| 6734 | + break; |
| 6735 | + |
| 6736 | + case OMAP_DSS_LCD_DISPLAY_TFT: |
| 6737 | + mode = 1; |
| 6738 | + break; |
| 6739 | + |
| 6740 | + default: |
| 6741 | + BUG(); |
| 6742 | + return; |
| 6743 | + } |
| 6744 | + |
| 6745 | + enable_clocks(1); |
| 6746 | + REG_FLD_MOD(DISPC_CONTROL, mode, 3, 3); |
| 6747 | + enable_clocks(0); |
| 6748 | +} |
| 6749 | + |
| 6750 | +void dispc_set_loadmode(enum omap_dss_load_mode mode) |
| 6751 | +{ |
| 6752 | + enable_clocks(1); |
| 6753 | + REG_FLD_MOD(DISPC_CONFIG, mode, 2, 1); |
| 6754 | + enable_clocks(0); |
| 6755 | +} |
| 6756 | + |
| 6757 | + |
| 6758 | +void dispc_set_default_color(enum omap_channel channel, u32 color) |
| 6759 | +{ |
| 6760 | + const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0, |
| 6761 | + DISPC_DEFAULT_COLOR1 }; |
| 6762 | + |
| 6763 | + enable_clocks(1); |
| 6764 | + dispc_write_reg(def_reg[channel], color); |
| 6765 | + enable_clocks(0); |
| 6766 | +} |
| 6767 | + |
| 6768 | +u32 dispc_get_default_color(enum omap_channel channel) |
| 6769 | +{ |
| 6770 | + const struct dispc_reg def_reg[] = { DISPC_DEFAULT_COLOR0, |
| 6771 | + DISPC_DEFAULT_COLOR1 }; |
| 6772 | + u32 l; |
| 6773 | + |
| 6774 | + BUG_ON(channel != OMAP_DSS_CHANNEL_DIGIT && |
| 6775 | + channel != OMAP_DSS_CHANNEL_LCD); |
| 6776 | + |
| 6777 | + enable_clocks(1); |
| 6778 | + l = dispc_read_reg(def_reg[channel]); |
| 6779 | + enable_clocks(0); |
| 6780 | + |
| 6781 | + return l; |
| 6782 | +} |
| 6783 | + |
| 6784 | +void dispc_set_trans_key(enum omap_channel ch, |
| 6785 | + enum omap_dss_trans_key_type type, |
| 6786 | + u32 trans_key) |
| 6787 | +{ |
| 6788 | + const struct dispc_reg tr_reg[] = { |
| 6789 | + DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 }; |
| 6790 | + |
| 6791 | + enable_clocks(1); |
| 6792 | + if (ch == OMAP_DSS_CHANNEL_LCD) |
| 6793 | + REG_FLD_MOD(DISPC_CONFIG, type, 11, 11); |
| 6794 | + else /* OMAP_DSS_CHANNEL_DIGIT */ |
| 6795 | + REG_FLD_MOD(DISPC_CONFIG, type, 13, 13); |
| 6796 | + |
| 6797 | + dispc_write_reg(tr_reg[ch], trans_key); |
| 6798 | + enable_clocks(0); |
| 6799 | +} |
| 6800 | + |
| 6801 | +void dispc_get_trans_key(enum omap_channel ch, |
| 6802 | + enum omap_dss_trans_key_type *type, |
| 6803 | + u32 *trans_key) |
| 6804 | +{ |
| 6805 | + const struct dispc_reg tr_reg[] = { |
| 6806 | + DISPC_TRANS_COLOR0, DISPC_TRANS_COLOR1 }; |
| 6807 | + |
| 6808 | + enable_clocks(1); |
| 6809 | + if (type) { |
| 6810 | + if (ch == OMAP_DSS_CHANNEL_LCD) |
| 6811 | + *type = REG_GET(DISPC_CONFIG, 11, 11); |
| 6812 | + else if (ch == OMAP_DSS_CHANNEL_DIGIT) |
| 6813 | + *type = REG_GET(DISPC_CONFIG, 13, 13); |
| 6814 | + else |
| 6815 | + BUG(); |
| 6816 | + } |
| 6817 | + |
| 6818 | + if (trans_key) |
| 6819 | + *trans_key = dispc_read_reg(tr_reg[ch]); |
| 6820 | + enable_clocks(0); |
| 6821 | +} |
| 6822 | + |
| 6823 | +void dispc_enable_trans_key(enum omap_channel ch, bool enable) |
| 6824 | +{ |
| 6825 | + enable_clocks(1); |
| 6826 | + if (ch == OMAP_DSS_CHANNEL_LCD) |
| 6827 | + REG_FLD_MOD(DISPC_CONFIG, enable, 10, 10); |
| 6828 | + else /* OMAP_DSS_CHANNEL_DIGIT */ |
| 6829 | + REG_FLD_MOD(DISPC_CONFIG, enable, 12, 12); |
| 6830 | + enable_clocks(0); |
| 6831 | +} |
| 6832 | +void dispc_enable_alpha_blending(enum omap_channel ch, bool enable) |
| 6833 | +{ |
| 6834 | + enable_clocks(1); |
| 6835 | + if (ch == OMAP_DSS_CHANNEL_LCD) |
| 6836 | + REG_FLD_MOD(DISPC_CONFIG, enable, 18, 18); |
| 6837 | + else /* OMAP_DSS_CHANNEL_DIGIT */ |
| 6838 | + REG_FLD_MOD(DISPC_CONFIG, enable, 19, 19); |
| 6839 | + enable_clocks(0); |
| 6840 | +} |
| 6841 | +bool dispc_alpha_blending_enabled(enum omap_channel ch) |
| 6842 | +{ |
| 6843 | + bool enabled; |
| 6844 | + |
| 6845 | + enable_clocks(1); |
| 6846 | + if (ch == OMAP_DSS_CHANNEL_LCD) |
| 6847 | + enabled = REG_GET(DISPC_CONFIG, 18, 18); |
| 6848 | + else if (ch == OMAP_DSS_CHANNEL_DIGIT) |
| 6849 | + enabled = REG_GET(DISPC_CONFIG, 18, 18); |
| 6850 | + else |
| 6851 | + BUG(); |
| 6852 | + enable_clocks(0); |
| 6853 | + |
| 6854 | + return enabled; |
| 6855 | + |
| 6856 | +} |
| 6857 | + |
| 6858 | + |
| 6859 | +bool dispc_trans_key_enabled(enum omap_channel ch) |
| 6860 | +{ |
| 6861 | + bool enabled; |
| 6862 | + |
| 6863 | + enable_clocks(1); |
| 6864 | + if (ch == OMAP_DSS_CHANNEL_LCD) |
| 6865 | + enabled = REG_GET(DISPC_CONFIG, 10, 10); |
| 6866 | + else if (ch == OMAP_DSS_CHANNEL_DIGIT) |
| 6867 | + enabled = REG_GET(DISPC_CONFIG, 12, 12); |
| 6868 | + else |
| 6869 | + BUG(); |
| 6870 | + enable_clocks(0); |
| 6871 | + |
| 6872 | + return enabled; |
| 6873 | +} |
| 6874 | + |
| 6875 | + |
| 6876 | +void dispc_set_tft_data_lines(u8 data_lines) |
| 6877 | +{ |
| 6878 | + int code; |
| 6879 | + |
| 6880 | + switch (data_lines) { |
| 6881 | + case 12: |
| 6882 | + code = 0; |
| 6883 | + break; |
| 6884 | + case 16: |
| 6885 | + code = 1; |
| 6886 | + break; |
| 6887 | + case 18: |
| 6888 | + code = 2; |
| 6889 | + break; |
| 6890 | + case 24: |
| 6891 | + code = 3; |
| 6892 | + break; |
| 6893 | + default: |
| 6894 | + BUG(); |
| 6895 | + return; |
| 6896 | + } |
| 6897 | + |
| 6898 | + enable_clocks(1); |
| 6899 | + REG_FLD_MOD(DISPC_CONTROL, code, 9, 8); |
| 6900 | + enable_clocks(0); |
| 6901 | +} |
| 6902 | + |
| 6903 | +void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode) |
| 6904 | +{ |
| 6905 | + u32 l; |
| 6906 | + int stallmode; |
| 6907 | + int gpout0 = 1; |
| 6908 | + int gpout1; |
| 6909 | + |
| 6910 | + switch (mode) { |
| 6911 | + case OMAP_DSS_PARALLELMODE_BYPASS: |
| 6912 | + stallmode = 0; |
| 6913 | + gpout1 = 1; |
| 6914 | + break; |
| 6915 | + |
| 6916 | + case OMAP_DSS_PARALLELMODE_RFBI: |
| 6917 | + stallmode = 1; |
| 6918 | + gpout1 = 0; |
| 6919 | + break; |
| 6920 | + |
| 6921 | + case OMAP_DSS_PARALLELMODE_DSI: |
| 6922 | + stallmode = 1; |
| 6923 | + gpout1 = 1; |
| 6924 | + break; |
| 6925 | + |
| 6926 | + default: |
| 6927 | + BUG(); |
| 6928 | + return; |
| 6929 | + } |
| 6930 | + |
| 6931 | + enable_clocks(1); |
| 6932 | + |
| 6933 | + l = dispc_read_reg(DISPC_CONTROL); |
| 6934 | + |
| 6935 | + l = FLD_MOD(l, stallmode, 11, 11); |
| 6936 | + l = FLD_MOD(l, gpout0, 15, 15); |
| 6937 | + l = FLD_MOD(l, gpout1, 16, 16); |
| 6938 | + |
| 6939 | + dispc_write_reg(DISPC_CONTROL, l); |
| 6940 | + |
| 6941 | + enable_clocks(0); |
| 6942 | +} |
| 6943 | + |
| 6944 | +static bool _dispc_lcd_timings_ok(int hsw, int hfp, int hbp, |
| 6945 | + int vsw, int vfp, int vbp) |
| 6946 | +{ |
| 6947 | + if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) { |
| 6948 | + if (hsw < 1 || hsw > 64 || |
| 6949 | + hfp < 1 || hfp > 256 || |
| 6950 | + hbp < 1 || hbp > 256 || |
| 6951 | + vsw < 1 || vsw > 64 || |
| 6952 | + vfp < 0 || vfp > 255 || |
| 6953 | + vbp < 0 || vbp > 255) |
| 6954 | + return false; |
| 6955 | + } else { |
| 6956 | + if (hsw < 1 || hsw > 256 || |
| 6957 | + hfp < 1 || hfp > 4096 || |
| 6958 | + hbp < 1 || hbp > 4096 || |
| 6959 | + vsw < 1 || vsw > 256 || |
| 6960 | + vfp < 0 || vfp > 4095 || |
| 6961 | + vbp < 0 || vbp > 4095) |
| 6962 | + return false; |
| 6963 | + } |
| 6964 | + |
| 6965 | + return true; |
| 6966 | +} |
| 6967 | + |
| 6968 | +bool dispc_lcd_timings_ok(struct omap_video_timings *timings) |
| 6969 | +{ |
| 6970 | + return _dispc_lcd_timings_ok(timings->hsw, timings->hfp, |
| 6971 | + timings->hbp, timings->vsw, |
| 6972 | + timings->vfp, timings->vbp); |
| 6973 | +} |
| 6974 | + |
| 6975 | +static void _dispc_set_lcd_timings(int hsw, int hfp, int hbp, |
| 6976 | + int vsw, int vfp, int vbp) |
| 6977 | +{ |
| 6978 | + u32 timing_h, timing_v; |
| 6979 | + |
| 6980 | + if (cpu_is_omap24xx() || omap_rev() < OMAP3430_REV_ES3_0) { |
| 6981 | + timing_h = FLD_VAL(hsw-1, 5, 0) | FLD_VAL(hfp-1, 15, 8) | |
| 6982 | + FLD_VAL(hbp-1, 27, 20); |
| 6983 | + |
| 6984 | + timing_v = FLD_VAL(vsw-1, 5, 0) | FLD_VAL(vfp, 15, 8) | |
| 6985 | + FLD_VAL(vbp, 27, 20); |
| 6986 | + } else { |
| 6987 | + timing_h = FLD_VAL(hsw-1, 7, 0) | FLD_VAL(hfp-1, 19, 8) | |
| 6988 | + FLD_VAL(hbp-1, 31, 20); |
| 6989 | + |
| 6990 | + timing_v = FLD_VAL(vsw-1, 7, 0) | FLD_VAL(vfp, 19, 8) | |
| 6991 | + FLD_VAL(vbp, 31, 20); |
| 6992 | + } |
| 6993 | + |
| 6994 | + enable_clocks(1); |
| 6995 | + dispc_write_reg(DISPC_TIMING_H, timing_h); |
| 6996 | + dispc_write_reg(DISPC_TIMING_V, timing_v); |
| 6997 | + enable_clocks(0); |
| 6998 | +} |
| 6999 | + |
| 7000 | +/* change name to mode? */ |
| 7001 | +void dispc_set_lcd_timings(struct omap_video_timings *timings) |
| 7002 | +{ |
| 7003 | + unsigned xtot, ytot; |
| 7004 | + unsigned long ht, vt; |
| 7005 | + |
| 7006 | + if (!_dispc_lcd_timings_ok(timings->hsw, timings->hfp, |
| 7007 | + timings->hbp, timings->vsw, |
| 7008 | + timings->vfp, timings->vbp)) |
| 7009 | + BUG(); |
| 7010 | + |
| 7011 | + _dispc_set_lcd_timings(timings->hsw, timings->hfp, timings->hbp, |
| 7012 | + timings->vsw, timings->vfp, timings->vbp); |
| 7013 | + |
| 7014 | + dispc_set_lcd_size(timings->x_res, timings->y_res); |
| 7015 | + |
| 7016 | + xtot = timings->x_res + timings->hfp + timings->hsw + timings->hbp; |
| 7017 | + ytot = timings->y_res + timings->vfp + timings->vsw + timings->vbp; |
| 7018 | + |
| 7019 | + ht = (timings->pixel_clock * 1000) / xtot; |
| 7020 | + vt = (timings->pixel_clock * 1000) / xtot / ytot; |
| 7021 | + |
| 7022 | + DSSDBG("xres %u yres %u\n", timings->x_res, timings->y_res); |
| 7023 | + DSSDBG("pck %u\n", timings->pixel_clock); |
| 7024 | + DSSDBG("hsw %d hfp %d hbp %d vsw %d vfp %d vbp %d\n", |
| 7025 | + timings->hsw, timings->hfp, timings->hbp, |
| 7026 | + timings->vsw, timings->vfp, timings->vbp); |
| 7027 | + |
| 7028 | + DSSDBG("hsync %luHz, vsync %luHz\n", ht, vt); |
| 7029 | +} |
| 7030 | + |
| 7031 | +void dispc_set_lcd_divisor(u16 lck_div, u16 pck_div) |
| 7032 | +{ |
| 7033 | + BUG_ON(lck_div < 1); |
| 7034 | + BUG_ON(pck_div < 2); |
| 7035 | + |
| 7036 | + enable_clocks(1); |
| 7037 | + dispc_write_reg(DISPC_DIVISOR, |
| 7038 | + FLD_VAL(lck_div, 23, 16) | FLD_VAL(pck_div, 7, 0)); |
| 7039 | + enable_clocks(0); |
| 7040 | +} |
| 7041 | + |
| 7042 | +static void dispc_get_lcd_divisor(int *lck_div, int *pck_div) |
| 7043 | +{ |
| 7044 | + u32 l; |
| 7045 | + l = dispc_read_reg(DISPC_DIVISOR); |
| 7046 | + *lck_div = FLD_GET(l, 23, 16); |
| 7047 | + *pck_div = FLD_GET(l, 7, 0); |
| 7048 | +} |
| 7049 | + |
| 7050 | +unsigned long dispc_fclk_rate(void) |
| 7051 | +{ |
| 7052 | + unsigned long r = 0; |
| 7053 | + |
| 7054 | + if (dss_get_dispc_clk_source() == 0) |
| 7055 | + r = dss_clk_get_rate(DSS_CLK_FCK1); |
| 7056 | + else |
| 7057 | +#ifdef CONFIG_OMAP2_DSS_DSI |
| 7058 | + r = dsi_get_dsi1_pll_rate(); |
| 7059 | +#else |
| 7060 | + BUG(); |
| 7061 | +#endif |
| 7062 | + return r; |
| 7063 | +} |
| 7064 | + |
| 7065 | +unsigned long dispc_lclk_rate(void) |
| 7066 | +{ |
| 7067 | + int lcd; |
| 7068 | + unsigned long r; |
| 7069 | + u32 l; |
| 7070 | + |
| 7071 | + l = dispc_read_reg(DISPC_DIVISOR); |
| 7072 | + |
| 7073 | + lcd = FLD_GET(l, 23, 16); |
| 7074 | + |
| 7075 | + r = dispc_fclk_rate(); |
| 7076 | + |
| 7077 | + return r / lcd; |
| 7078 | +} |
| 7079 | + |
| 7080 | +unsigned long dispc_pclk_rate(void) |
| 7081 | +{ |
| 7082 | + int lcd, pcd; |
| 7083 | + unsigned long r; |
| 7084 | + u32 l; |
| 7085 | + |
| 7086 | + l = dispc_read_reg(DISPC_DIVISOR); |
| 7087 | + |
| 7088 | + lcd = FLD_GET(l, 23, 16); |
| 7089 | + pcd = FLD_GET(l, 7, 0); |
| 7090 | + |
| 7091 | + r = dispc_fclk_rate(); |
| 7092 | + |
| 7093 | + return r / lcd / pcd; |
| 7094 | +} |
| 7095 | + |
| 7096 | +void dispc_dump_clocks(struct seq_file *s) |
| 7097 | +{ |
| 7098 | + int lcd, pcd; |
| 7099 | + |
| 7100 | + enable_clocks(1); |
| 7101 | + |
| 7102 | + dispc_get_lcd_divisor(&lcd, &pcd); |
| 7103 | + |
| 7104 | + seq_printf(s, "- dispc -\n"); |
| 7105 | + |
| 7106 | + seq_printf(s, "dispc fclk source = %s\n", |
| 7107 | + dss_get_dispc_clk_source() == 0 ? |
| 7108 | + "dss1_alwon_fclk" : "dsi1_pll_fclk"); |
| 7109 | + |
| 7110 | + seq_printf(s, "pixel clk = %lu / %d / %d = %lu\n", |
| 7111 | + dispc_fclk_rate(), |
| 7112 | + lcd, pcd, |
| 7113 | + dispc_pclk_rate()); |
| 7114 | + |
| 7115 | + enable_clocks(0); |
| 7116 | +} |
| 7117 | + |
| 7118 | +void dispc_dump_regs(struct seq_file *s) |
| 7119 | +{ |
| 7120 | +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dispc_read_reg(r)) |
| 7121 | + |
| 7122 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 7123 | + |
| 7124 | + DUMPREG(DISPC_REVISION); |
| 7125 | + DUMPREG(DISPC_SYSCONFIG); |
| 7126 | + DUMPREG(DISPC_SYSSTATUS); |
| 7127 | + DUMPREG(DISPC_IRQSTATUS); |
| 7128 | + DUMPREG(DISPC_IRQENABLE); |
| 7129 | + DUMPREG(DISPC_CONTROL); |
| 7130 | + DUMPREG(DISPC_CONFIG); |
| 7131 | + DUMPREG(DISPC_CAPABLE); |
| 7132 | + DUMPREG(DISPC_DEFAULT_COLOR0); |
| 7133 | + DUMPREG(DISPC_DEFAULT_COLOR1); |
| 7134 | + DUMPREG(DISPC_TRANS_COLOR0); |
| 7135 | + DUMPREG(DISPC_TRANS_COLOR1); |
| 7136 | + DUMPREG(DISPC_LINE_STATUS); |
| 7137 | + DUMPREG(DISPC_LINE_NUMBER); |
| 7138 | + DUMPREG(DISPC_TIMING_H); |
| 7139 | + DUMPREG(DISPC_TIMING_V); |
| 7140 | + DUMPREG(DISPC_POL_FREQ); |
| 7141 | + DUMPREG(DISPC_DIVISOR); |
| 7142 | + DUMPREG(DISPC_GLOBAL_ALPHA); |
| 7143 | + DUMPREG(DISPC_SIZE_DIG); |
| 7144 | + DUMPREG(DISPC_SIZE_LCD); |
| 7145 | + |
| 7146 | + DUMPREG(DISPC_GFX_BA0); |
| 7147 | + DUMPREG(DISPC_GFX_BA1); |
| 7148 | + DUMPREG(DISPC_GFX_POSITION); |
| 7149 | + DUMPREG(DISPC_GFX_SIZE); |
| 7150 | + DUMPREG(DISPC_GFX_ATTRIBUTES); |
| 7151 | + DUMPREG(DISPC_GFX_FIFO_THRESHOLD); |
| 7152 | + DUMPREG(DISPC_GFX_FIFO_SIZE_STATUS); |
| 7153 | + DUMPREG(DISPC_GFX_ROW_INC); |
| 7154 | + DUMPREG(DISPC_GFX_PIXEL_INC); |
| 7155 | + DUMPREG(DISPC_GFX_WINDOW_SKIP); |
| 7156 | + DUMPREG(DISPC_GFX_TABLE_BA); |
| 7157 | + |
| 7158 | + DUMPREG(DISPC_DATA_CYCLE1); |
| 7159 | + DUMPREG(DISPC_DATA_CYCLE2); |
| 7160 | + DUMPREG(DISPC_DATA_CYCLE3); |
| 7161 | + |
| 7162 | + DUMPREG(DISPC_CPR_COEF_R); |
| 7163 | + DUMPREG(DISPC_CPR_COEF_G); |
| 7164 | + DUMPREG(DISPC_CPR_COEF_B); |
| 7165 | + |
| 7166 | + DUMPREG(DISPC_GFX_PRELOAD); |
| 7167 | + |
| 7168 | + DUMPREG(DISPC_VID_BA0(0)); |
| 7169 | + DUMPREG(DISPC_VID_BA1(0)); |
| 7170 | + DUMPREG(DISPC_VID_POSITION(0)); |
| 7171 | + DUMPREG(DISPC_VID_SIZE(0)); |
| 7172 | + DUMPREG(DISPC_VID_ATTRIBUTES(0)); |
| 7173 | + DUMPREG(DISPC_VID_FIFO_THRESHOLD(0)); |
| 7174 | + DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(0)); |
| 7175 | + DUMPREG(DISPC_VID_ROW_INC(0)); |
| 7176 | + DUMPREG(DISPC_VID_PIXEL_INC(0)); |
| 7177 | + DUMPREG(DISPC_VID_FIR(0)); |
| 7178 | + DUMPREG(DISPC_VID_PICTURE_SIZE(0)); |
| 7179 | + DUMPREG(DISPC_VID_ACCU0(0)); |
| 7180 | + DUMPREG(DISPC_VID_ACCU1(0)); |
| 7181 | + |
| 7182 | + DUMPREG(DISPC_VID_BA0(1)); |
| 7183 | + DUMPREG(DISPC_VID_BA1(1)); |
| 7184 | + DUMPREG(DISPC_VID_POSITION(1)); |
| 7185 | + DUMPREG(DISPC_VID_SIZE(1)); |
| 7186 | + DUMPREG(DISPC_VID_ATTRIBUTES(1)); |
| 7187 | + DUMPREG(DISPC_VID_FIFO_THRESHOLD(1)); |
| 7188 | + DUMPREG(DISPC_VID_FIFO_SIZE_STATUS(1)); |
| 7189 | + DUMPREG(DISPC_VID_ROW_INC(1)); |
| 7190 | + DUMPREG(DISPC_VID_PIXEL_INC(1)); |
| 7191 | + DUMPREG(DISPC_VID_FIR(1)); |
| 7192 | + DUMPREG(DISPC_VID_PICTURE_SIZE(1)); |
| 7193 | + DUMPREG(DISPC_VID_ACCU0(1)); |
| 7194 | + DUMPREG(DISPC_VID_ACCU1(1)); |
| 7195 | + |
| 7196 | + DUMPREG(DISPC_VID_FIR_COEF_H(0, 0)); |
| 7197 | + DUMPREG(DISPC_VID_FIR_COEF_H(0, 1)); |
| 7198 | + DUMPREG(DISPC_VID_FIR_COEF_H(0, 2)); |
| 7199 | + DUMPREG(DISPC_VID_FIR_COEF_H(0, 3)); |
| 7200 | + DUMPREG(DISPC_VID_FIR_COEF_H(0, 4)); |
| 7201 | + DUMPREG(DISPC_VID_FIR_COEF_H(0, 5)); |
| 7202 | + DUMPREG(DISPC_VID_FIR_COEF_H(0, 6)); |
| 7203 | + DUMPREG(DISPC_VID_FIR_COEF_H(0, 7)); |
| 7204 | + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 0)); |
| 7205 | + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 1)); |
| 7206 | + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 2)); |
| 7207 | + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 3)); |
| 7208 | + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 4)); |
| 7209 | + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 5)); |
| 7210 | + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 6)); |
| 7211 | + DUMPREG(DISPC_VID_FIR_COEF_HV(0, 7)); |
| 7212 | + DUMPREG(DISPC_VID_CONV_COEF(0, 0)); |
| 7213 | + DUMPREG(DISPC_VID_CONV_COEF(0, 1)); |
| 7214 | + DUMPREG(DISPC_VID_CONV_COEF(0, 2)); |
| 7215 | + DUMPREG(DISPC_VID_CONV_COEF(0, 3)); |
| 7216 | + DUMPREG(DISPC_VID_CONV_COEF(0, 4)); |
| 7217 | + DUMPREG(DISPC_VID_FIR_COEF_V(0, 0)); |
| 7218 | + DUMPREG(DISPC_VID_FIR_COEF_V(0, 1)); |
| 7219 | + DUMPREG(DISPC_VID_FIR_COEF_V(0, 2)); |
| 7220 | + DUMPREG(DISPC_VID_FIR_COEF_V(0, 3)); |
| 7221 | + DUMPREG(DISPC_VID_FIR_COEF_V(0, 4)); |
| 7222 | + DUMPREG(DISPC_VID_FIR_COEF_V(0, 5)); |
| 7223 | + DUMPREG(DISPC_VID_FIR_COEF_V(0, 6)); |
| 7224 | + DUMPREG(DISPC_VID_FIR_COEF_V(0, 7)); |
| 7225 | + |
| 7226 | + DUMPREG(DISPC_VID_FIR_COEF_H(1, 0)); |
| 7227 | + DUMPREG(DISPC_VID_FIR_COEF_H(1, 1)); |
| 7228 | + DUMPREG(DISPC_VID_FIR_COEF_H(1, 2)); |
| 7229 | + DUMPREG(DISPC_VID_FIR_COEF_H(1, 3)); |
| 7230 | + DUMPREG(DISPC_VID_FIR_COEF_H(1, 4)); |
| 7231 | + DUMPREG(DISPC_VID_FIR_COEF_H(1, 5)); |
| 7232 | + DUMPREG(DISPC_VID_FIR_COEF_H(1, 6)); |
| 7233 | + DUMPREG(DISPC_VID_FIR_COEF_H(1, 7)); |
| 7234 | + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 0)); |
| 7235 | + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 1)); |
| 7236 | + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 2)); |
| 7237 | + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 3)); |
| 7238 | + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 4)); |
| 7239 | + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 5)); |
| 7240 | + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 6)); |
| 7241 | + DUMPREG(DISPC_VID_FIR_COEF_HV(1, 7)); |
| 7242 | + DUMPREG(DISPC_VID_CONV_COEF(1, 0)); |
| 7243 | + DUMPREG(DISPC_VID_CONV_COEF(1, 1)); |
| 7244 | + DUMPREG(DISPC_VID_CONV_COEF(1, 2)); |
| 7245 | + DUMPREG(DISPC_VID_CONV_COEF(1, 3)); |
| 7246 | + DUMPREG(DISPC_VID_CONV_COEF(1, 4)); |
| 7247 | + DUMPREG(DISPC_VID_FIR_COEF_V(1, 0)); |
| 7248 | + DUMPREG(DISPC_VID_FIR_COEF_V(1, 1)); |
| 7249 | + DUMPREG(DISPC_VID_FIR_COEF_V(1, 2)); |
| 7250 | + DUMPREG(DISPC_VID_FIR_COEF_V(1, 3)); |
| 7251 | + DUMPREG(DISPC_VID_FIR_COEF_V(1, 4)); |
| 7252 | + DUMPREG(DISPC_VID_FIR_COEF_V(1, 5)); |
| 7253 | + DUMPREG(DISPC_VID_FIR_COEF_V(1, 6)); |
| 7254 | + DUMPREG(DISPC_VID_FIR_COEF_V(1, 7)); |
| 7255 | + |
| 7256 | + DUMPREG(DISPC_VID_PRELOAD(0)); |
| 7257 | + DUMPREG(DISPC_VID_PRELOAD(1)); |
| 7258 | + |
| 7259 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 7260 | +#undef DUMPREG |
| 7261 | +} |
| 7262 | + |
| 7263 | +static void _dispc_set_pol_freq(bool onoff, bool rf, bool ieo, bool ipc, |
| 7264 | + bool ihs, bool ivs, u8 acbi, u8 acb) |
| 7265 | +{ |
| 7266 | + u32 l = 0; |
| 7267 | + |
| 7268 | + DSSDBG("onoff %d rf %d ieo %d ipc %d ihs %d ivs %d acbi %d acb %d\n", |
| 7269 | + onoff, rf, ieo, ipc, ihs, ivs, acbi, acb); |
| 7270 | + |
| 7271 | + l |= FLD_VAL(onoff, 17, 17); |
| 7272 | + l |= FLD_VAL(rf, 16, 16); |
| 7273 | + l |= FLD_VAL(ieo, 15, 15); |
| 7274 | + l |= FLD_VAL(ipc, 14, 14); |
| 7275 | + l |= FLD_VAL(ihs, 13, 13); |
| 7276 | + l |= FLD_VAL(ivs, 12, 12); |
| 7277 | + l |= FLD_VAL(acbi, 11, 8); |
| 7278 | + l |= FLD_VAL(acb, 7, 0); |
| 7279 | + |
| 7280 | + enable_clocks(1); |
| 7281 | + dispc_write_reg(DISPC_POL_FREQ, l); |
| 7282 | + enable_clocks(0); |
| 7283 | +} |
| 7284 | + |
| 7285 | +void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb) |
| 7286 | +{ |
| 7287 | + _dispc_set_pol_freq((config & OMAP_DSS_LCD_ONOFF) != 0, |
| 7288 | + (config & OMAP_DSS_LCD_RF) != 0, |
| 7289 | + (config & OMAP_DSS_LCD_IEO) != 0, |
| 7290 | + (config & OMAP_DSS_LCD_IPC) != 0, |
| 7291 | + (config & OMAP_DSS_LCD_IHS) != 0, |
| 7292 | + (config & OMAP_DSS_LCD_IVS) != 0, |
| 7293 | + acbi, acb); |
| 7294 | +} |
| 7295 | + |
| 7296 | +void find_lck_pck_divs(bool is_tft, unsigned long req_pck, unsigned long fck, |
| 7297 | + u16 *lck_div, u16 *pck_div) |
| 7298 | +{ |
| 7299 | + u16 pcd_min = is_tft ? 2 : 3; |
| 7300 | + unsigned long best_pck; |
| 7301 | + u16 best_ld, cur_ld; |
| 7302 | + u16 best_pd, cur_pd; |
| 7303 | + |
| 7304 | + best_pck = 0; |
| 7305 | + best_ld = 0; |
| 7306 | + best_pd = 0; |
| 7307 | + |
| 7308 | + for (cur_ld = 1; cur_ld <= 255; ++cur_ld) { |
| 7309 | + unsigned long lck = fck / cur_ld; |
| 7310 | + |
| 7311 | + for (cur_pd = pcd_min; cur_pd <= 255; ++cur_pd) { |
| 7312 | + unsigned long pck = lck / cur_pd; |
| 7313 | + long old_delta = abs(best_pck - req_pck); |
| 7314 | + long new_delta = abs(pck - req_pck); |
| 7315 | + |
| 7316 | + if (best_pck == 0 || new_delta < old_delta) { |
| 7317 | + best_pck = pck; |
| 7318 | + best_ld = cur_ld; |
| 7319 | + best_pd = cur_pd; |
| 7320 | + |
| 7321 | + if (pck == req_pck) |
| 7322 | + goto found; |
| 7323 | + } |
| 7324 | + |
| 7325 | + if (pck < req_pck) |
| 7326 | + break; |
| 7327 | + } |
| 7328 | + |
| 7329 | + if (lck / pcd_min < req_pck) |
| 7330 | + break; |
| 7331 | + } |
| 7332 | + |
| 7333 | +found: |
| 7334 | + *lck_div = best_ld; |
| 7335 | + *pck_div = best_pd; |
| 7336 | +} |
| 7337 | + |
| 7338 | +int dispc_calc_clock_div(bool is_tft, unsigned long req_pck, |
| 7339 | + struct dispc_clock_info *cinfo) |
| 7340 | +{ |
| 7341 | + unsigned long prate; |
| 7342 | + struct dispc_clock_info cur, best; |
| 7343 | + int match = 0; |
| 7344 | + int min_fck_per_pck; |
| 7345 | + unsigned long fck_rate = dss_clk_get_rate(DSS_CLK_FCK1); |
| 7346 | + |
| 7347 | + if (cpu_is_omap34xx()) |
| 7348 | + prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); |
| 7349 | + else |
| 7350 | + prate = 0; |
| 7351 | + |
| 7352 | + if (req_pck == dispc.cache_req_pck && |
| 7353 | + ((cpu_is_omap34xx() && prate == dispc.cache_prate) || |
| 7354 | + dispc.cache_cinfo.fck == fck_rate)) { |
| 7355 | + DSSDBG("dispc clock info found from cache.\n"); |
| 7356 | + *cinfo = dispc.cache_cinfo; |
| 7357 | + return 0; |
| 7358 | + } |
| 7359 | + |
| 7360 | + min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK; |
| 7361 | + |
| 7362 | + if (min_fck_per_pck && |
| 7363 | + req_pck * min_fck_per_pck > DISPC_MAX_FCK) { |
| 7364 | + DSSERR("Requested pixel clock not possible with the current " |
| 7365 | + "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning " |
| 7366 | + "the constraint off.\n"); |
| 7367 | + min_fck_per_pck = 0; |
| 7368 | + } |
| 7369 | + |
| 7370 | +retry: |
| 7371 | + memset(&cur, 0, sizeof(cur)); |
| 7372 | + memset(&best, 0, sizeof(best)); |
| 7373 | + |
| 7374 | + if (cpu_is_omap24xx()) { |
| 7375 | + /* XXX can we change the clock on omap2? */ |
| 7376 | + cur.fck = dss_clk_get_rate(DSS_CLK_FCK1); |
| 7377 | + cur.fck_div = 1; |
| 7378 | + |
| 7379 | + match = 1; |
| 7380 | + |
| 7381 | + find_lck_pck_divs(is_tft, req_pck, cur.fck, |
| 7382 | + &cur.lck_div, &cur.pck_div); |
| 7383 | + |
| 7384 | + cur.lck = cur.fck / cur.lck_div; |
| 7385 | + cur.pck = cur.lck / cur.pck_div; |
| 7386 | + |
| 7387 | + best = cur; |
| 7388 | + |
| 7389 | + goto found; |
| 7390 | + } else if (cpu_is_omap34xx()) { |
| 7391 | + for (cur.fck_div = 16; cur.fck_div > 0; --cur.fck_div) { |
| 7392 | + cur.fck = prate / cur.fck_div * 2; |
| 7393 | + |
| 7394 | + if (cur.fck > DISPC_MAX_FCK) |
| 7395 | + continue; |
| 7396 | + |
| 7397 | + if (min_fck_per_pck && |
| 7398 | + cur.fck < req_pck * min_fck_per_pck) |
| 7399 | + continue; |
| 7400 | + |
| 7401 | + match = 1; |
| 7402 | + |
| 7403 | + find_lck_pck_divs(is_tft, req_pck, cur.fck, |
| 7404 | + &cur.lck_div, &cur.pck_div); |
| 7405 | + |
| 7406 | + cur.lck = cur.fck / cur.lck_div; |
| 7407 | + cur.pck = cur.lck / cur.pck_div; |
| 7408 | + |
| 7409 | + if (abs(cur.pck - req_pck) < abs(best.pck - req_pck)) { |
| 7410 | + best = cur; |
| 7411 | + |
| 7412 | + if (cur.pck == req_pck) |
| 7413 | + goto found; |
| 7414 | + } |
| 7415 | + } |
| 7416 | + } else { |
| 7417 | + BUG(); |
| 7418 | + } |
| 7419 | + |
| 7420 | +found: |
| 7421 | + if (!match) { |
| 7422 | + if (min_fck_per_pck) { |
| 7423 | + DSSERR("Could not find suitable clock settings.\n" |
| 7424 | + "Turning FCK/PCK constraint off and" |
| 7425 | + "trying again.\n"); |
| 7426 | + min_fck_per_pck = 0; |
| 7427 | + goto retry; |
| 7428 | + } |
| 7429 | + |
| 7430 | + DSSERR("Could not find suitable clock settings.\n"); |
| 7431 | + |
| 7432 | + return -EINVAL; |
| 7433 | + } |
| 7434 | + |
| 7435 | + if (cinfo) |
| 7436 | + *cinfo = best; |
| 7437 | + |
| 7438 | + dispc.cache_req_pck = req_pck; |
| 7439 | + dispc.cache_prate = prate; |
| 7440 | + dispc.cache_cinfo = best; |
| 7441 | + |
| 7442 | + return 0; |
| 7443 | +} |
| 7444 | + |
| 7445 | +int dispc_set_clock_div(struct dispc_clock_info *cinfo) |
| 7446 | +{ |
| 7447 | + unsigned long prate; |
| 7448 | + int r; |
| 7449 | + |
| 7450 | + if (cpu_is_omap34xx()) { |
| 7451 | + prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); |
| 7452 | + DSSDBG("dpll4_m4 = %ld\n", prate); |
| 7453 | + } |
| 7454 | + |
| 7455 | + DSSDBG("fck = %ld (%d)\n", cinfo->fck, cinfo->fck_div); |
| 7456 | + DSSDBG("lck = %ld (%d)\n", cinfo->lck, cinfo->lck_div); |
| 7457 | + DSSDBG("pck = %ld (%d)\n", cinfo->pck, cinfo->pck_div); |
| 7458 | + |
| 7459 | + if (cpu_is_omap34xx()) { |
| 7460 | + r = clk_set_rate(dispc.dpll4_m4_ck, prate / cinfo->fck_div); |
| 7461 | + if (r) |
| 7462 | + return r; |
| 7463 | + } |
| 7464 | + |
| 7465 | + dispc_set_lcd_divisor(cinfo->lck_div, cinfo->pck_div); |
| 7466 | + |
| 7467 | + return 0; |
| 7468 | +} |
| 7469 | + |
| 7470 | +int dispc_get_clock_div(struct dispc_clock_info *cinfo) |
| 7471 | +{ |
| 7472 | + cinfo->fck = dss_clk_get_rate(DSS_CLK_FCK1); |
| 7473 | + |
| 7474 | + if (cpu_is_omap34xx()) { |
| 7475 | + unsigned long prate; |
| 7476 | + prate = clk_get_rate(clk_get_parent(dispc.dpll4_m4_ck)); |
| 7477 | + cinfo->fck_div = prate / (cinfo->fck / 2); |
| 7478 | + } else { |
| 7479 | + cinfo->fck_div = 0; |
| 7480 | + } |
| 7481 | + |
| 7482 | + cinfo->lck_div = REG_GET(DISPC_DIVISOR, 23, 16); |
| 7483 | + cinfo->pck_div = REG_GET(DISPC_DIVISOR, 7, 0); |
| 7484 | + |
| 7485 | + cinfo->lck = cinfo->fck / cinfo->lck_div; |
| 7486 | + cinfo->pck = cinfo->lck / cinfo->pck_div; |
| 7487 | + |
| 7488 | + return 0; |
| 7489 | +} |
| 7490 | + |
| 7491 | +/* dispc.irq_lock has to be locked by the caller */ |
| 7492 | +static void _omap_dispc_set_irqs(void) |
| 7493 | +{ |
| 7494 | + u32 mask; |
| 7495 | + u32 old_mask; |
| 7496 | + int i; |
| 7497 | + struct omap_dispc_isr_data *isr_data; |
| 7498 | + |
| 7499 | + mask = dispc.irq_error_mask; |
| 7500 | + |
| 7501 | + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { |
| 7502 | + isr_data = &dispc.registered_isr[i]; |
| 7503 | + |
| 7504 | + if (isr_data->isr == NULL) |
| 7505 | + continue; |
| 7506 | + |
| 7507 | + mask |= isr_data->mask; |
| 7508 | + } |
| 7509 | + |
| 7510 | + enable_clocks(1); |
| 7511 | + |
| 7512 | + old_mask = dispc_read_reg(DISPC_IRQENABLE); |
| 7513 | + /* clear the irqstatus for newly enabled irqs */ |
| 7514 | + dispc_write_reg(DISPC_IRQSTATUS, (mask ^ old_mask) & mask); |
| 7515 | + |
| 7516 | + dispc_write_reg(DISPC_IRQENABLE, mask); |
| 7517 | + |
| 7518 | + enable_clocks(0); |
| 7519 | +} |
| 7520 | + |
| 7521 | +int omap_dispc_register_isr(omap_dispc_isr_t isr, void *arg, u32 mask) |
| 7522 | +{ |
| 7523 | + int i; |
| 7524 | + int ret; |
| 7525 | + unsigned long flags; |
| 7526 | + struct omap_dispc_isr_data *isr_data; |
| 7527 | + |
| 7528 | + if (isr == NULL) |
| 7529 | + return -EINVAL; |
| 7530 | + |
| 7531 | + spin_lock_irqsave(&dispc.irq_lock, flags); |
| 7532 | + |
| 7533 | + /* check for duplicate entry */ |
| 7534 | + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { |
| 7535 | + isr_data = &dispc.registered_isr[i]; |
| 7536 | + if (isr_data->isr == isr && isr_data->arg == arg && |
| 7537 | + isr_data->mask == mask) { |
| 7538 | + ret = -EINVAL; |
| 7539 | + goto err; |
| 7540 | + } |
| 7541 | + } |
| 7542 | + |
| 7543 | + isr_data = NULL; |
| 7544 | + ret = -EBUSY; |
| 7545 | + |
| 7546 | + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { |
| 7547 | + isr_data = &dispc.registered_isr[i]; |
| 7548 | + |
| 7549 | + if (isr_data->isr != NULL) |
| 7550 | + continue; |
| 7551 | + |
| 7552 | + isr_data->isr = isr; |
| 7553 | + isr_data->arg = arg; |
| 7554 | + isr_data->mask = mask; |
| 7555 | + ret = 0; |
| 7556 | + |
| 7557 | + break; |
| 7558 | + } |
| 7559 | + |
| 7560 | + _omap_dispc_set_irqs(); |
| 7561 | + |
| 7562 | + spin_unlock_irqrestore(&dispc.irq_lock, flags); |
| 7563 | + |
| 7564 | + return 0; |
| 7565 | +err: |
| 7566 | + spin_unlock_irqrestore(&dispc.irq_lock, flags); |
| 7567 | + |
| 7568 | + return ret; |
| 7569 | +} |
| 7570 | +EXPORT_SYMBOL(omap_dispc_register_isr); |
| 7571 | + |
| 7572 | +int omap_dispc_unregister_isr(omap_dispc_isr_t isr, void *arg, u32 mask) |
| 7573 | +{ |
| 7574 | + int i; |
| 7575 | + unsigned long flags; |
| 7576 | + int ret = -EINVAL; |
| 7577 | + struct omap_dispc_isr_data *isr_data; |
| 7578 | + |
| 7579 | + spin_lock_irqsave(&dispc.irq_lock, flags); |
| 7580 | + |
| 7581 | + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { |
| 7582 | + isr_data = &dispc.registered_isr[i]; |
| 7583 | + if (isr_data->isr != isr || isr_data->arg != arg || |
| 7584 | + isr_data->mask != mask) |
| 7585 | + continue; |
| 7586 | + |
| 7587 | + /* found the correct isr */ |
| 7588 | + |
| 7589 | + isr_data->isr = NULL; |
| 7590 | + isr_data->arg = NULL; |
| 7591 | + isr_data->mask = 0; |
| 7592 | + |
| 7593 | + ret = 0; |
| 7594 | + break; |
| 7595 | + } |
| 7596 | + |
| 7597 | + if (ret == 0) |
| 7598 | + _omap_dispc_set_irqs(); |
| 7599 | + |
| 7600 | + spin_unlock_irqrestore(&dispc.irq_lock, flags); |
| 7601 | + |
| 7602 | + return ret; |
| 7603 | +} |
| 7604 | +EXPORT_SYMBOL(omap_dispc_unregister_isr); |
| 7605 | + |
| 7606 | +#ifdef DEBUG |
| 7607 | +static void print_irq_status(u32 status) |
| 7608 | +{ |
| 7609 | + if ((status & dispc.irq_error_mask) == 0) |
| 7610 | + return; |
| 7611 | + |
| 7612 | + printk(KERN_DEBUG "DISPC IRQ: 0x%x: ", status); |
| 7613 | + |
| 7614 | +#define PIS(x) \ |
| 7615 | + if (status & DISPC_IRQ_##x) \ |
| 7616 | + printk(#x " "); |
| 7617 | + PIS(GFX_FIFO_UNDERFLOW); |
| 7618 | + PIS(OCP_ERR); |
| 7619 | + PIS(VID1_FIFO_UNDERFLOW); |
| 7620 | + PIS(VID2_FIFO_UNDERFLOW); |
| 7621 | + PIS(SYNC_LOST); |
| 7622 | + PIS(SYNC_LOST_DIGIT); |
| 7623 | +#undef PIS |
| 7624 | + |
| 7625 | + printk("\n"); |
| 7626 | +} |
| 7627 | +#endif |
| 7628 | + |
| 7629 | +/* Called from dss.c. Note that we don't touch clocks here, |
| 7630 | + * but we presume they are on because we got an IRQ. However, |
| 7631 | + * an irq handler may turn the clocks off, so we may not have |
| 7632 | + * clock later in the function. */ |
| 7633 | +void dispc_irq_handler(void) |
| 7634 | +{ |
| 7635 | + int i; |
| 7636 | + u32 irqstatus; |
| 7637 | + u32 handledirqs = 0; |
| 7638 | + u32 unhandled_errors; |
| 7639 | + struct omap_dispc_isr_data *isr_data; |
| 7640 | + struct omap_dispc_isr_data registered_isr[DISPC_MAX_NR_ISRS]; |
| 7641 | + |
| 7642 | + spin_lock(&dispc.irq_lock); |
| 7643 | + |
| 7644 | + irqstatus = dispc_read_reg(DISPC_IRQSTATUS); |
| 7645 | + |
| 7646 | +#ifdef DEBUG |
| 7647 | + if (dss_debug) |
| 7648 | + print_irq_status(irqstatus); |
| 7649 | +#endif |
| 7650 | + /* Ack the interrupt. Do it here before clocks are possibly turned |
| 7651 | + * off */ |
| 7652 | + dispc_write_reg(DISPC_IRQSTATUS, irqstatus); |
| 7653 | + |
| 7654 | + /* make a copy and unlock, so that isrs can unregister |
| 7655 | + * themselves */ |
| 7656 | + memcpy(registered_isr, dispc.registered_isr, |
| 7657 | + sizeof(registered_isr)); |
| 7658 | + |
| 7659 | + spin_unlock(&dispc.irq_lock); |
| 7660 | + |
| 7661 | + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { |
| 7662 | + isr_data = ®istered_isr[i]; |
| 7663 | + |
| 7664 | + if (!isr_data->isr) |
| 7665 | + continue; |
| 7666 | + |
| 7667 | + if (isr_data->mask & irqstatus) { |
| 7668 | + isr_data->isr(isr_data->arg, irqstatus); |
| 7669 | + handledirqs |= isr_data->mask; |
| 7670 | + } |
| 7671 | + } |
| 7672 | + |
| 7673 | + spin_lock(&dispc.irq_lock); |
| 7674 | + |
| 7675 | + unhandled_errors = irqstatus & ~handledirqs & dispc.irq_error_mask; |
| 7676 | + |
| 7677 | + if (unhandled_errors) { |
| 7678 | + dispc.error_irqs |= unhandled_errors; |
| 7679 | + |
| 7680 | + dispc.irq_error_mask &= ~unhandled_errors; |
| 7681 | + _omap_dispc_set_irqs(); |
| 7682 | + |
| 7683 | + schedule_work(&dispc.error_work); |
| 7684 | + } |
| 7685 | + |
| 7686 | + spin_unlock(&dispc.irq_lock); |
| 7687 | +} |
| 7688 | + |
| 7689 | +static void dispc_error_worker(struct work_struct *work) |
| 7690 | +{ |
| 7691 | + int i; |
| 7692 | + u32 errors; |
| 7693 | + unsigned long flags; |
| 7694 | + |
| 7695 | + spin_lock_irqsave(&dispc.irq_lock, flags); |
| 7696 | + errors = dispc.error_irqs; |
| 7697 | + dispc.error_irqs = 0; |
| 7698 | + spin_unlock_irqrestore(&dispc.irq_lock, flags); |
| 7699 | + |
| 7700 | + if (errors & DISPC_IRQ_GFX_FIFO_UNDERFLOW) { |
| 7701 | + DSSERR("GFX_FIFO_UNDERFLOW, disabling GFX\n"); |
| 7702 | + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { |
| 7703 | + struct omap_overlay *ovl; |
| 7704 | + ovl = omap_dss_get_overlay(i); |
| 7705 | + |
| 7706 | + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) |
| 7707 | + continue; |
| 7708 | + |
| 7709 | + if (ovl->id == 0) { |
| 7710 | + dispc_enable_plane(ovl->id, 0); |
| 7711 | + dispc_go(ovl->manager->id); |
| 7712 | + mdelay(50); |
| 7713 | + break; |
| 7714 | + } |
| 7715 | + } |
| 7716 | + } |
| 7717 | + |
| 7718 | + if (errors & DISPC_IRQ_VID1_FIFO_UNDERFLOW) { |
| 7719 | + DSSERR("VID1_FIFO_UNDERFLOW, disabling VID1\n"); |
| 7720 | + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { |
| 7721 | + struct omap_overlay *ovl; |
| 7722 | + ovl = omap_dss_get_overlay(i); |
| 7723 | + |
| 7724 | + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) |
| 7725 | + continue; |
| 7726 | + |
| 7727 | + if (ovl->id == 1) { |
| 7728 | + dispc_enable_plane(ovl->id, 0); |
| 7729 | + dispc_go(ovl->manager->id); |
| 7730 | + mdelay(50); |
| 7731 | + break; |
| 7732 | + } |
| 7733 | + } |
| 7734 | + } |
| 7735 | + |
| 7736 | + if (errors & DISPC_IRQ_VID2_FIFO_UNDERFLOW) { |
| 7737 | + DSSERR("VID2_FIFO_UNDERFLOW, disabling VID2\n"); |
| 7738 | + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { |
| 7739 | + struct omap_overlay *ovl; |
| 7740 | + ovl = omap_dss_get_overlay(i); |
| 7741 | + |
| 7742 | + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) |
| 7743 | + continue; |
| 7744 | + |
| 7745 | + if (ovl->id == 2) { |
| 7746 | + dispc_enable_plane(ovl->id, 0); |
| 7747 | + dispc_go(ovl->manager->id); |
| 7748 | + mdelay(50); |
| 7749 | + break; |
| 7750 | + } |
| 7751 | + } |
| 7752 | + } |
| 7753 | + |
| 7754 | + if (errors & DISPC_IRQ_SYNC_LOST) { |
| 7755 | + struct omap_overlay_manager *manager = NULL; |
| 7756 | + bool enable = false; |
| 7757 | + |
| 7758 | + DSSERR("SYNC_LOST, disabling LCD\n"); |
| 7759 | + |
| 7760 | + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { |
| 7761 | + struct omap_overlay_manager *mgr; |
| 7762 | + mgr = omap_dss_get_overlay_manager(i); |
| 7763 | + |
| 7764 | + if (mgr->id == OMAP_DSS_CHANNEL_LCD) { |
| 7765 | + manager = mgr; |
| 7766 | + enable = mgr->device->state == |
| 7767 | + OMAP_DSS_DISPLAY_ACTIVE; |
| 7768 | + mgr->device->disable(mgr->device); |
| 7769 | + break; |
| 7770 | + } |
| 7771 | + } |
| 7772 | + |
| 7773 | + if (manager) { |
| 7774 | + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { |
| 7775 | + struct omap_overlay *ovl; |
| 7776 | + ovl = omap_dss_get_overlay(i); |
| 7777 | + |
| 7778 | + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) |
| 7779 | + continue; |
| 7780 | + |
| 7781 | + if (ovl->id != 0 && ovl->manager == manager) |
| 7782 | + dispc_enable_plane(ovl->id, 0); |
| 7783 | + } |
| 7784 | + |
| 7785 | + dispc_go(manager->id); |
| 7786 | + mdelay(50); |
| 7787 | + if (enable) |
| 7788 | + manager->device->enable(manager->device); |
| 7789 | + } |
| 7790 | + } |
| 7791 | + |
| 7792 | + if (errors & DISPC_IRQ_SYNC_LOST_DIGIT) { |
| 7793 | + struct omap_overlay_manager *manager = NULL; |
| 7794 | + bool enable = false; |
| 7795 | + |
| 7796 | + DSSERR("SYNC_LOST_DIGIT, disabling TV\n"); |
| 7797 | + |
| 7798 | + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { |
| 7799 | + struct omap_overlay_manager *mgr; |
| 7800 | + mgr = omap_dss_get_overlay_manager(i); |
| 7801 | + |
| 7802 | + if (mgr->id == OMAP_DSS_CHANNEL_DIGIT) { |
| 7803 | + manager = mgr; |
| 7804 | + enable = mgr->device->state == |
| 7805 | + OMAP_DSS_DISPLAY_ACTIVE; |
| 7806 | + mgr->device->disable(mgr->device); |
| 7807 | + break; |
| 7808 | + } |
| 7809 | + } |
| 7810 | + |
| 7811 | + if (manager) { |
| 7812 | + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { |
| 7813 | + struct omap_overlay *ovl; |
| 7814 | + ovl = omap_dss_get_overlay(i); |
| 7815 | + |
| 7816 | + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) |
| 7817 | + continue; |
| 7818 | + |
| 7819 | + if (ovl->id != 0 && ovl->manager == manager) |
| 7820 | + dispc_enable_plane(ovl->id, 0); |
| 7821 | + } |
| 7822 | + |
| 7823 | + dispc_go(manager->id); |
| 7824 | + mdelay(50); |
| 7825 | + if (enable) |
| 7826 | + manager->device->enable(manager->device); |
| 7827 | + } |
| 7828 | + } |
| 7829 | + |
| 7830 | + if (errors & DISPC_IRQ_OCP_ERR) { |
| 7831 | + DSSERR("OCP_ERR\n"); |
| 7832 | + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { |
| 7833 | + struct omap_overlay_manager *mgr; |
| 7834 | + mgr = omap_dss_get_overlay_manager(i); |
| 7835 | + |
| 7836 | + if (mgr->caps & OMAP_DSS_OVL_CAP_DISPC) |
| 7837 | + mgr->device->disable(mgr->device); |
| 7838 | + } |
| 7839 | + } |
| 7840 | + |
| 7841 | + spin_lock_irqsave(&dispc.irq_lock, flags); |
| 7842 | + dispc.irq_error_mask |= errors; |
| 7843 | + _omap_dispc_set_irqs(); |
| 7844 | + spin_unlock_irqrestore(&dispc.irq_lock, flags); |
| 7845 | +} |
| 7846 | + |
| 7847 | +int omap_dispc_wait_for_irq_timeout(u32 irqmask, unsigned long timeout) |
| 7848 | +{ |
| 7849 | + void dispc_irq_wait_handler(void *data, u32 mask) |
| 7850 | + { |
| 7851 | + complete((struct completion *)data); |
| 7852 | + } |
| 7853 | + |
| 7854 | + int r; |
| 7855 | + DECLARE_COMPLETION_ONSTACK(completion); |
| 7856 | + |
| 7857 | + r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion, |
| 7858 | + irqmask); |
| 7859 | + |
| 7860 | + if (r) |
| 7861 | + return r; |
| 7862 | + |
| 7863 | + timeout = wait_for_completion_timeout(&completion, timeout); |
| 7864 | + |
| 7865 | + omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask); |
| 7866 | + |
| 7867 | + if (timeout == 0) |
| 7868 | + return -ETIMEDOUT; |
| 7869 | + |
| 7870 | + if (timeout == -ERESTARTSYS) |
| 7871 | + return -ERESTARTSYS; |
| 7872 | + |
| 7873 | + return 0; |
| 7874 | +} |
| 7875 | + |
| 7876 | +int omap_dispc_wait_for_irq_interruptible_timeout(u32 irqmask, |
| 7877 | + unsigned long timeout) |
| 7878 | +{ |
| 7879 | + void dispc_irq_wait_handler(void *data, u32 mask) |
| 7880 | + { |
| 7881 | + complete((struct completion *)data); |
| 7882 | + } |
| 7883 | + |
| 7884 | + int r; |
| 7885 | + DECLARE_COMPLETION_ONSTACK(completion); |
| 7886 | + |
| 7887 | + r = omap_dispc_register_isr(dispc_irq_wait_handler, &completion, |
| 7888 | + irqmask); |
| 7889 | + |
| 7890 | + if (r) |
| 7891 | + return r; |
| 7892 | + |
| 7893 | + timeout = wait_for_completion_interruptible_timeout(&completion, |
| 7894 | + timeout); |
| 7895 | + |
| 7896 | + omap_dispc_unregister_isr(dispc_irq_wait_handler, &completion, irqmask); |
| 7897 | + |
| 7898 | + if (timeout == 0) |
| 7899 | + return -ETIMEDOUT; |
| 7900 | + |
| 7901 | + if (timeout == -ERESTARTSYS) |
| 7902 | + return -ERESTARTSYS; |
| 7903 | + |
| 7904 | + return 0; |
| 7905 | +} |
| 7906 | + |
| 7907 | +#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC |
| 7908 | +void dispc_fake_vsync_irq(void) |
| 7909 | +{ |
| 7910 | + u32 irqstatus = DISPC_IRQ_VSYNC; |
| 7911 | + int i; |
| 7912 | + |
| 7913 | + for (i = 0; i < DISPC_MAX_NR_ISRS; i++) { |
| 7914 | + struct omap_dispc_isr_data *isr_data; |
| 7915 | + isr_data = &dispc.registered_isr[i]; |
| 7916 | + |
| 7917 | + if (!isr_data->isr) |
| 7918 | + continue; |
| 7919 | + |
| 7920 | + if (isr_data->mask & irqstatus) |
| 7921 | + isr_data->isr(isr_data->arg, irqstatus); |
| 7922 | + } |
| 7923 | +} |
| 7924 | +#endif |
| 7925 | + |
| 7926 | +static void _omap_dispc_initialize_irq(void) |
| 7927 | +{ |
| 7928 | + unsigned long flags; |
| 7929 | + |
| 7930 | + spin_lock_irqsave(&dispc.irq_lock, flags); |
| 7931 | + |
| 7932 | + memset(dispc.registered_isr, 0, sizeof(dispc.registered_isr)); |
| 7933 | + |
| 7934 | + dispc.irq_error_mask = DISPC_IRQ_MASK_ERROR; |
| 7935 | + |
| 7936 | + /* there's SYNC_LOST_DIGIT waiting after enabling the DSS, |
| 7937 | + * so clear it */ |
| 7938 | + dispc_write_reg(DISPC_IRQSTATUS, dispc_read_reg(DISPC_IRQSTATUS)); |
| 7939 | + |
| 7940 | + _omap_dispc_set_irqs(); |
| 7941 | + |
| 7942 | + spin_unlock_irqrestore(&dispc.irq_lock, flags); |
| 7943 | +} |
| 7944 | + |
| 7945 | +void dispc_enable_sidle(void) |
| 7946 | +{ |
| 7947 | + REG_FLD_MOD(DISPC_SYSCONFIG, 2, 4, 3); /* SIDLEMODE: smart idle */ |
| 7948 | +} |
| 7949 | + |
| 7950 | +void dispc_disable_sidle(void) |
| 7951 | +{ |
| 7952 | + REG_FLD_MOD(DISPC_SYSCONFIG, 1, 4, 3); /* SIDLEMODE: no idle */ |
| 7953 | +} |
| 7954 | + |
| 7955 | +static void _omap_dispc_initial_config(void) |
| 7956 | +{ |
| 7957 | + u32 l; |
| 7958 | + |
| 7959 | + l = dispc_read_reg(DISPC_SYSCONFIG); |
| 7960 | + l = FLD_MOD(l, 2, 13, 12); /* MIDLEMODE: smart standby */ |
| 7961 | + l = FLD_MOD(l, 2, 4, 3); /* SIDLEMODE: smart idle */ |
| 7962 | + l = FLD_MOD(l, 1, 2, 2); /* ENWAKEUP */ |
| 7963 | + l = FLD_MOD(l, 1, 0, 0); /* AUTOIDLE */ |
| 7964 | + dispc_write_reg(DISPC_SYSCONFIG, l); |
| 7965 | + |
| 7966 | + /* FUNCGATED */ |
| 7967 | + REG_FLD_MOD(DISPC_CONFIG, 1, 9, 9); |
| 7968 | + |
| 7969 | + /* L3 firewall setting: enable access to OCM RAM */ |
| 7970 | + if (cpu_is_omap24xx()) |
| 7971 | + __raw_writel(0x402000b0, OMAP2_IO_ADDRESS(0x680050a0)); |
| 7972 | + |
| 7973 | + _dispc_setup_color_conv_coef(); |
| 7974 | + |
| 7975 | + dispc_set_loadmode(OMAP_DSS_LOAD_FRAME_ONLY); |
| 7976 | + |
| 7977 | + dispc_read_plane_fifo_sizes(); |
| 7978 | +} |
| 7979 | + |
| 7980 | +int dispc_init(void) |
| 7981 | +{ |
| 7982 | + u32 rev; |
| 7983 | + |
| 7984 | + spin_lock_init(&dispc.irq_lock); |
| 7985 | + |
| 7986 | + INIT_WORK(&dispc.error_work, dispc_error_worker); |
| 7987 | + |
| 7988 | + dispc.base = ioremap(DISPC_BASE, DISPC_SZ_REGS); |
| 7989 | + if (!dispc.base) { |
| 7990 | + DSSERR("can't ioremap DISPC\n"); |
| 7991 | + return -ENOMEM; |
| 7992 | + } |
| 7993 | + |
| 7994 | + if (cpu_is_omap34xx()) { |
| 7995 | + dispc.dpll4_m4_ck = clk_get(NULL, "dpll4_m4_ck"); |
| 7996 | + if (IS_ERR(dispc.dpll4_m4_ck)) { |
| 7997 | + DSSERR("Failed to get dpll4_m4_ck\n"); |
| 7998 | + return -ENODEV; |
| 7999 | + } |
| 8000 | + } |
| 8001 | + |
| 8002 | + enable_clocks(1); |
| 8003 | + |
| 8004 | + _omap_dispc_initial_config(); |
| 8005 | + |
| 8006 | + _omap_dispc_initialize_irq(); |
| 8007 | + |
| 8008 | + dispc_save_context(); |
| 8009 | + |
| 8010 | + rev = dispc_read_reg(DISPC_REVISION); |
| 8011 | + printk(KERN_INFO "OMAP DISPC rev %d.%d\n", |
| 8012 | + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); |
| 8013 | + |
| 8014 | + enable_clocks(0); |
| 8015 | + |
| 8016 | + return 0; |
| 8017 | +} |
| 8018 | + |
| 8019 | +void dispc_exit(void) |
| 8020 | +{ |
| 8021 | + if (cpu_is_omap34xx()) |
| 8022 | + clk_put(dispc.dpll4_m4_ck); |
| 8023 | + iounmap(dispc.base); |
| 8024 | +} |
| 8025 | + |
| 8026 | +int dispc_enable_plane(enum omap_plane plane, bool enable) |
| 8027 | +{ |
| 8028 | + DSSDBG("dispc_enable_plane %d, %d\n", plane, enable); |
| 8029 | + |
| 8030 | + enable_clocks(1); |
| 8031 | + _dispc_enable_plane(plane, enable); |
| 8032 | + enable_clocks(0); |
| 8033 | + |
| 8034 | + return 0; |
| 8035 | +} |
| 8036 | + |
| 8037 | +int dispc_setup_plane(enum omap_plane plane, |
| 8038 | + u32 paddr, u16 screen_width, |
| 8039 | + u16 pos_x, u16 pos_y, |
| 8040 | + u16 width, u16 height, |
| 8041 | + u16 out_width, u16 out_height, |
| 8042 | + enum omap_color_mode color_mode, |
| 8043 | + bool ilace, |
| 8044 | + enum omap_dss_rotation_type rotation_type, |
| 8045 | + u8 rotation, bool mirror, u8 global_alpha) |
| 8046 | +{ |
| 8047 | + int r = 0; |
| 8048 | + |
| 8049 | + DSSDBG("dispc_setup_plane %d, pa %x, sw %d, %d,%d, %dx%d -> " |
| 8050 | + "%dx%d, ilace %d, cmode %x, rot %d, mir %d\n", |
| 8051 | + plane, paddr, screen_width, pos_x, pos_y, |
| 8052 | + width, height, |
| 8053 | + out_width, out_height, |
| 8054 | + ilace, color_mode, |
| 8055 | + rotation, mirror); |
| 8056 | + |
| 8057 | + enable_clocks(1); |
| 8058 | + |
| 8059 | + r = _dispc_setup_plane(plane, |
| 8060 | + paddr, screen_width, |
| 8061 | + pos_x, pos_y, |
| 8062 | + width, height, |
| 8063 | + out_width, out_height, |
| 8064 | + color_mode, ilace, |
| 8065 | + rotation_type, |
| 8066 | + rotation, mirror, |
| 8067 | + global_alpha); |
| 8068 | + |
| 8069 | + enable_clocks(0); |
| 8070 | + |
| 8071 | + return r; |
| 8072 | +} |
| 8073 | --- /dev/null |
| 8074 | +++ b/drivers/video/omap2/dss/display.c |
| 8075 | @@ -0,0 +1,658 @@ |
| 8076 | +/* |
| 8077 | + * linux/drivers/video/omap2/dss/display.c |
| 8078 | + * |
| 8079 | + * Copyright (C) 2009 Nokia Corporation |
| 8080 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 8081 | + * |
| 8082 | + * Some code and ideas taken from drivers/video/omap/ driver |
| 8083 | + * by Imre Deak. |
| 8084 | + * |
| 8085 | + * This program is free software; you can redistribute it and/or modify it |
| 8086 | + * under the terms of the GNU General Public License version 2 as published by |
| 8087 | + * the Free Software Foundation. |
| 8088 | + * |
| 8089 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 8090 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 8091 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 8092 | + * more details. |
| 8093 | + * |
| 8094 | + * You should have received a copy of the GNU General Public License along with |
| 8095 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 8096 | + */ |
| 8097 | + |
| 8098 | +#define DSS_SUBSYS_NAME "DISPLAY" |
| 8099 | + |
| 8100 | +#include <linux/kernel.h> |
| 8101 | +#include <linux/module.h> |
| 8102 | +#include <linux/jiffies.h> |
| 8103 | +#include <linux/list.h> |
| 8104 | +#include <linux/platform_device.h> |
| 8105 | + |
| 8106 | +#include <mach/display.h> |
| 8107 | +#include "dss.h" |
| 8108 | + |
| 8109 | +static LIST_HEAD(display_list); |
| 8110 | + |
| 8111 | +static ssize_t display_enabled_show(struct device *dev, |
| 8112 | + struct device_attribute *attr, char *buf) |
| 8113 | +{ |
| 8114 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8115 | + bool enabled = dssdev->state != OMAP_DSS_DISPLAY_DISABLED; |
| 8116 | + |
| 8117 | + return snprintf(buf, PAGE_SIZE, "%d\n", enabled); |
| 8118 | +} |
| 8119 | + |
| 8120 | +static ssize_t display_enabled_store(struct device *dev, |
| 8121 | + struct device_attribute *attr, |
| 8122 | + const char *buf, size_t size) |
| 8123 | +{ |
| 8124 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8125 | + bool enabled, r; |
| 8126 | + |
| 8127 | + enabled = simple_strtoul(buf, NULL, 10); |
| 8128 | + |
| 8129 | + if (enabled != (dssdev->state != OMAP_DSS_DISPLAY_DISABLED)) { |
| 8130 | + if (enabled) { |
| 8131 | + r = dssdev->enable(dssdev); |
| 8132 | + if (r) |
| 8133 | + return r; |
| 8134 | + } else { |
| 8135 | + dssdev->disable(dssdev); |
| 8136 | + } |
| 8137 | + } |
| 8138 | + |
| 8139 | + return size; |
| 8140 | +} |
| 8141 | + |
| 8142 | +static ssize_t display_upd_mode_show(struct device *dev, |
| 8143 | + struct device_attribute *attr, char *buf) |
| 8144 | +{ |
| 8145 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8146 | + enum omap_dss_update_mode mode = OMAP_DSS_UPDATE_AUTO; |
| 8147 | + if (dssdev->get_update_mode) |
| 8148 | + mode = dssdev->get_update_mode(dssdev); |
| 8149 | + return snprintf(buf, PAGE_SIZE, "%d\n", mode); |
| 8150 | +} |
| 8151 | + |
| 8152 | +static ssize_t display_upd_mode_store(struct device *dev, |
| 8153 | + struct device_attribute *attr, |
| 8154 | + const char *buf, size_t size) |
| 8155 | +{ |
| 8156 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8157 | + int val, r; |
| 8158 | + enum omap_dss_update_mode mode; |
| 8159 | + |
| 8160 | + val = simple_strtoul(buf, NULL, 10); |
| 8161 | + |
| 8162 | + switch (val) { |
| 8163 | + case OMAP_DSS_UPDATE_DISABLED: |
| 8164 | + case OMAP_DSS_UPDATE_AUTO: |
| 8165 | + case OMAP_DSS_UPDATE_MANUAL: |
| 8166 | + mode = (enum omap_dss_update_mode)val; |
| 8167 | + break; |
| 8168 | + default: |
| 8169 | + return -EINVAL; |
| 8170 | + } |
| 8171 | + |
| 8172 | + r = dssdev->set_update_mode(dssdev, mode); |
| 8173 | + if (r) |
| 8174 | + return r; |
| 8175 | + |
| 8176 | + return size; |
| 8177 | +} |
| 8178 | + |
| 8179 | +static ssize_t display_tear_show(struct device *dev, |
| 8180 | + struct device_attribute *attr, char *buf) |
| 8181 | +{ |
| 8182 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8183 | + return snprintf(buf, PAGE_SIZE, "%d\n", |
| 8184 | + dssdev->get_te ? dssdev->get_te(dssdev) : 0); |
| 8185 | +} |
| 8186 | + |
| 8187 | +static ssize_t display_tear_store(struct device *dev, |
| 8188 | + struct device_attribute *attr, const char *buf, size_t size) |
| 8189 | +{ |
| 8190 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8191 | + unsigned long te; |
| 8192 | + int r; |
| 8193 | + |
| 8194 | + if (!dssdev->enable_te || !dssdev->get_te) |
| 8195 | + return -ENOENT; |
| 8196 | + |
| 8197 | + te = simple_strtoul(buf, NULL, 0); |
| 8198 | + |
| 8199 | + r = dssdev->enable_te(dssdev, te); |
| 8200 | + if (r) |
| 8201 | + return r; |
| 8202 | + |
| 8203 | + return size; |
| 8204 | +} |
| 8205 | + |
| 8206 | +static ssize_t display_timings_show(struct device *dev, |
| 8207 | + struct device_attribute *attr, char *buf) |
| 8208 | +{ |
| 8209 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8210 | + struct omap_video_timings t; |
| 8211 | + |
| 8212 | + if (!dssdev->get_timings) |
| 8213 | + return -ENOENT; |
| 8214 | + |
| 8215 | + dssdev->get_timings(dssdev, &t); |
| 8216 | + |
| 8217 | + return snprintf(buf, PAGE_SIZE, "%u,%u/%u/%u/%u,%u/%u/%u/%u\n", |
| 8218 | + t.pixel_clock, |
| 8219 | + t.x_res, t.hfp, t.hbp, t.hsw, |
| 8220 | + t.y_res, t.vfp, t.vbp, t.vsw); |
| 8221 | +} |
| 8222 | + |
| 8223 | +static ssize_t display_timings_store(struct device *dev, |
| 8224 | + struct device_attribute *attr, const char *buf, size_t size) |
| 8225 | +{ |
| 8226 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8227 | + struct omap_video_timings t; |
| 8228 | + int r, found; |
| 8229 | + |
| 8230 | + if (!dssdev->set_timings || !dssdev->check_timings) |
| 8231 | + return -ENOENT; |
| 8232 | + |
| 8233 | + found = 0; |
| 8234 | +#ifdef CONFIG_OMAP2_DSS_VENC |
| 8235 | + if (strncmp("pal", buf, 3) == 0) { |
| 8236 | + t = omap_dss_pal_timings; |
| 8237 | + found = 1; |
| 8238 | + } else if (strncmp("ntsc", buf, 4) == 0) { |
| 8239 | + t = omap_dss_ntsc_timings; |
| 8240 | + found = 1; |
| 8241 | + } |
| 8242 | +#endif |
| 8243 | + if (!found && sscanf(buf, "%u,%hu/%hu/%hu/%hu,%hu/%hu/%hu/%hu", |
| 8244 | + &t.pixel_clock, |
| 8245 | + &t.x_res, &t.hfp, &t.hbp, &t.hsw, |
| 8246 | + &t.y_res, &t.vfp, &t.vbp, &t.vsw) != 9) |
| 8247 | + return -EINVAL; |
| 8248 | + |
| 8249 | + r = dssdev->check_timings(dssdev, &t); |
| 8250 | + if (r) |
| 8251 | + return r; |
| 8252 | + |
| 8253 | + dssdev->set_timings(dssdev, &t); |
| 8254 | + |
| 8255 | + return size; |
| 8256 | +} |
| 8257 | + |
| 8258 | +static ssize_t display_rotate_show(struct device *dev, |
| 8259 | + struct device_attribute *attr, char *buf) |
| 8260 | +{ |
| 8261 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8262 | + int rotate; |
| 8263 | + if (!dssdev->get_rotate) |
| 8264 | + return -ENOENT; |
| 8265 | + rotate = dssdev->get_rotate(dssdev); |
| 8266 | + return snprintf(buf, PAGE_SIZE, "%u\n", rotate); |
| 8267 | +} |
| 8268 | + |
| 8269 | +static ssize_t display_rotate_store(struct device *dev, |
| 8270 | + struct device_attribute *attr, const char *buf, size_t size) |
| 8271 | +{ |
| 8272 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8273 | + unsigned long rot; |
| 8274 | + int r; |
| 8275 | + |
| 8276 | + if (!dssdev->set_rotate || !dssdev->get_rotate) |
| 8277 | + return -ENOENT; |
| 8278 | + |
| 8279 | + rot = simple_strtoul(buf, NULL, 0); |
| 8280 | + |
| 8281 | + r = dssdev->set_rotate(dssdev, rot); |
| 8282 | + if (r) |
| 8283 | + return r; |
| 8284 | + |
| 8285 | + return size; |
| 8286 | +} |
| 8287 | + |
| 8288 | +static ssize_t display_mirror_show(struct device *dev, |
| 8289 | + struct device_attribute *attr, char *buf) |
| 8290 | +{ |
| 8291 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8292 | + int mirror; |
| 8293 | + if (!dssdev->get_mirror) |
| 8294 | + return -ENOENT; |
| 8295 | + mirror = dssdev->get_mirror(dssdev); |
| 8296 | + return snprintf(buf, PAGE_SIZE, "%u\n", mirror); |
| 8297 | +} |
| 8298 | + |
| 8299 | +static ssize_t display_mirror_store(struct device *dev, |
| 8300 | + struct device_attribute *attr, const char *buf, size_t size) |
| 8301 | +{ |
| 8302 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8303 | + unsigned long mirror; |
| 8304 | + int r; |
| 8305 | + |
| 8306 | + if (!dssdev->set_mirror || !dssdev->get_mirror) |
| 8307 | + return -ENOENT; |
| 8308 | + |
| 8309 | + mirror = simple_strtoul(buf, NULL, 0); |
| 8310 | + |
| 8311 | + r = dssdev->set_mirror(dssdev, mirror); |
| 8312 | + if (r) |
| 8313 | + return r; |
| 8314 | + |
| 8315 | + return size; |
| 8316 | +} |
| 8317 | + |
| 8318 | +static ssize_t display_wss_show(struct device *dev, |
| 8319 | + struct device_attribute *attr, char *buf) |
| 8320 | +{ |
| 8321 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8322 | + unsigned int wss; |
| 8323 | + |
| 8324 | + if (!dssdev->get_wss) |
| 8325 | + return -ENOENT; |
| 8326 | + |
| 8327 | + wss = dssdev->get_wss(dssdev); |
| 8328 | + |
| 8329 | + return snprintf(buf, PAGE_SIZE, "0x%05x\n", wss); |
| 8330 | +} |
| 8331 | + |
| 8332 | +static ssize_t display_wss_store(struct device *dev, |
| 8333 | + struct device_attribute *attr, const char *buf, size_t size) |
| 8334 | +{ |
| 8335 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8336 | + unsigned long wss; |
| 8337 | + int r; |
| 8338 | + |
| 8339 | + if (!dssdev->get_wss || !dssdev->set_wss) |
| 8340 | + return -ENOENT; |
| 8341 | + |
| 8342 | + if (strict_strtoul(buf, 0, &wss)) |
| 8343 | + return -EINVAL; |
| 8344 | + |
| 8345 | + if (wss > 0xfffff) |
| 8346 | + return -EINVAL; |
| 8347 | + |
| 8348 | + r = dssdev->set_wss(dssdev, wss); |
| 8349 | + if (r) |
| 8350 | + return r; |
| 8351 | + |
| 8352 | + return size; |
| 8353 | +} |
| 8354 | + |
| 8355 | +static DEVICE_ATTR(enabled, S_IRUGO|S_IWUSR, |
| 8356 | + display_enabled_show, display_enabled_store); |
| 8357 | +static DEVICE_ATTR(update_mode, S_IRUGO|S_IWUSR, |
| 8358 | + display_upd_mode_show, display_upd_mode_store); |
| 8359 | +static DEVICE_ATTR(tear_elim, S_IRUGO|S_IWUSR, |
| 8360 | + display_tear_show, display_tear_store); |
| 8361 | +static DEVICE_ATTR(timings, S_IRUGO|S_IWUSR, |
| 8362 | + display_timings_show, display_timings_store); |
| 8363 | +static DEVICE_ATTR(rotate, S_IRUGO|S_IWUSR, |
| 8364 | + display_rotate_show, display_rotate_store); |
| 8365 | +static DEVICE_ATTR(mirror, S_IRUGO|S_IWUSR, |
| 8366 | + display_mirror_show, display_mirror_store); |
| 8367 | +static DEVICE_ATTR(wss, S_IRUGO|S_IWUSR, |
| 8368 | + display_wss_show, display_wss_store); |
| 8369 | + |
| 8370 | +static struct device_attribute *display_sysfs_attrs[] = { |
| 8371 | + &dev_attr_enabled, |
| 8372 | + &dev_attr_update_mode, |
| 8373 | + &dev_attr_tear_elim, |
| 8374 | + &dev_attr_timings, |
| 8375 | + &dev_attr_rotate, |
| 8376 | + &dev_attr_mirror, |
| 8377 | + &dev_attr_wss, |
| 8378 | + NULL |
| 8379 | +}; |
| 8380 | + |
| 8381 | +static void default_get_resolution(struct omap_dss_device *dssdev, |
| 8382 | + u16 *xres, u16 *yres) |
| 8383 | +{ |
| 8384 | + *xres = dssdev->panel.timings.x_res; |
| 8385 | + *yres = dssdev->panel.timings.y_res; |
| 8386 | +} |
| 8387 | + |
| 8388 | +void default_get_overlay_fifo_thresholds(enum omap_plane plane, |
| 8389 | + u32 fifo_size, enum omap_burst_size *burst_size, |
| 8390 | + u32 *fifo_low, u32 *fifo_high) |
| 8391 | +{ |
| 8392 | + unsigned burst_size_bytes; |
| 8393 | + |
| 8394 | + *burst_size = OMAP_DSS_BURST_16x32; |
| 8395 | + burst_size_bytes = 16 * 32 / 8; |
| 8396 | + |
| 8397 | + *fifo_high = fifo_size - 1; |
| 8398 | + *fifo_low = fifo_size - burst_size_bytes; |
| 8399 | +} |
| 8400 | + |
| 8401 | +static int default_wait_vsync(struct omap_dss_device *dssdev) |
| 8402 | +{ |
| 8403 | + unsigned long timeout = msecs_to_jiffies(500); |
| 8404 | + u32 irq; |
| 8405 | + |
| 8406 | + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) |
| 8407 | + irq = DISPC_IRQ_EVSYNC_ODD; |
| 8408 | + else |
| 8409 | + irq = DISPC_IRQ_VSYNC; |
| 8410 | + |
| 8411 | + return omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); |
| 8412 | +} |
| 8413 | + |
| 8414 | +static int default_get_recommended_bpp(struct omap_dss_device *dssdev) |
| 8415 | +{ |
| 8416 | + if (dssdev->panel.recommended_bpp) |
| 8417 | + return dssdev->panel.recommended_bpp; |
| 8418 | + |
| 8419 | + switch (dssdev->type) { |
| 8420 | + case OMAP_DISPLAY_TYPE_DPI: |
| 8421 | + if (dssdev->phy.dpi.data_lines == 24) |
| 8422 | + return 24; |
| 8423 | + else |
| 8424 | + return 16; |
| 8425 | + |
| 8426 | + case OMAP_DISPLAY_TYPE_DBI: |
| 8427 | + case OMAP_DISPLAY_TYPE_DSI: |
| 8428 | + if (dssdev->ctrl.pixel_size == 24) |
| 8429 | + return 24; |
| 8430 | + else |
| 8431 | + return 16; |
| 8432 | + case OMAP_DISPLAY_TYPE_VENC: |
| 8433 | + case OMAP_DISPLAY_TYPE_SDI: |
| 8434 | + return 24; |
| 8435 | + return 24; |
| 8436 | + default: |
| 8437 | + BUG(); |
| 8438 | + } |
| 8439 | +} |
| 8440 | + |
| 8441 | +/* Checks if replication logic should be used. Only use for active matrix, |
| 8442 | + * when overlay is in RGB12U or RGB16 mode, and LCD interface is |
| 8443 | + * 18bpp or 24bpp */ |
| 8444 | +bool dss_use_replication(struct omap_dss_device *dssdev, |
| 8445 | + enum omap_color_mode mode) |
| 8446 | +{ |
| 8447 | + int bpp; |
| 8448 | + |
| 8449 | + if (mode != OMAP_DSS_COLOR_RGB12U && mode != OMAP_DSS_COLOR_RGB16) |
| 8450 | + return false; |
| 8451 | + |
| 8452 | + if (dssdev->type == OMAP_DISPLAY_TYPE_DPI && |
| 8453 | + (dssdev->panel.config & OMAP_DSS_LCD_TFT) == 0) |
| 8454 | + return false; |
| 8455 | + |
| 8456 | + switch (dssdev->type) { |
| 8457 | + case OMAP_DISPLAY_TYPE_DPI: |
| 8458 | + bpp = dssdev->phy.dpi.data_lines; |
| 8459 | + break; |
| 8460 | + case OMAP_DISPLAY_TYPE_VENC: |
| 8461 | + case OMAP_DISPLAY_TYPE_SDI: |
| 8462 | + bpp = 24; |
| 8463 | + break; |
| 8464 | + case OMAP_DISPLAY_TYPE_DBI: |
| 8465 | + case OMAP_DISPLAY_TYPE_DSI: |
| 8466 | + bpp = dssdev->ctrl.pixel_size; |
| 8467 | + break; |
| 8468 | + default: |
| 8469 | + BUG(); |
| 8470 | + } |
| 8471 | + |
| 8472 | + return bpp > 16; |
| 8473 | +} |
| 8474 | + |
| 8475 | +void dss_init_device(struct platform_device *pdev, |
| 8476 | + struct omap_dss_device *dssdev) |
| 8477 | +{ |
| 8478 | + struct device_attribute *attr; |
| 8479 | + int i; |
| 8480 | + int r; |
| 8481 | + |
| 8482 | + switch (dssdev->type) { |
| 8483 | + case OMAP_DISPLAY_TYPE_DPI: |
| 8484 | +#ifdef CONFIG_OMAP2_DSS_RFBI |
| 8485 | + case OMAP_DISPLAY_TYPE_DBI: |
| 8486 | +#endif |
| 8487 | +#ifdef CONFIG_OMAP2_DSS_SDI |
| 8488 | + case OMAP_DISPLAY_TYPE_SDI: |
| 8489 | +#endif |
| 8490 | +#ifdef CONFIG_OMAP2_DSS_DSI |
| 8491 | + case OMAP_DISPLAY_TYPE_DSI: |
| 8492 | +#endif |
| 8493 | +#ifdef CONFIG_OMAP2_DSS_VENC |
| 8494 | + case OMAP_DISPLAY_TYPE_VENC: |
| 8495 | +#endif |
| 8496 | + break; |
| 8497 | + default: |
| 8498 | + DSSERR("Support for display '%s' not compiled in.\n", |
| 8499 | + dssdev->name); |
| 8500 | + return; |
| 8501 | + } |
| 8502 | + |
| 8503 | + dssdev->get_resolution = default_get_resolution; |
| 8504 | + dssdev->get_recommended_bpp = default_get_recommended_bpp; |
| 8505 | + dssdev->wait_vsync = default_wait_vsync; |
| 8506 | + |
| 8507 | + switch (dssdev->type) { |
| 8508 | + case OMAP_DISPLAY_TYPE_DPI: |
| 8509 | + r = dpi_init_display(dssdev); |
| 8510 | + break; |
| 8511 | +#ifdef CONFIG_OMAP2_DSS_RFBI |
| 8512 | + case OMAP_DISPLAY_TYPE_DBI: |
| 8513 | + r = rfbi_init_display(dssdev); |
| 8514 | + break; |
| 8515 | +#endif |
| 8516 | +#ifdef CONFIG_OMAP2_DSS_VENC |
| 8517 | + case OMAP_DISPLAY_TYPE_VENC: |
| 8518 | + r = venc_init_display(dssdev); |
| 8519 | + break; |
| 8520 | +#endif |
| 8521 | +#ifdef CONFIG_OMAP2_DSS_SDI |
| 8522 | + case OMAP_DISPLAY_TYPE_SDI: |
| 8523 | + r = sdi_init_display(dssdev); |
| 8524 | + break; |
| 8525 | +#endif |
| 8526 | +#ifdef CONFIG_OMAP2_DSS_DSI |
| 8527 | + case OMAP_DISPLAY_TYPE_DSI: |
| 8528 | + r = dsi_init_display(dssdev); |
| 8529 | + break; |
| 8530 | +#endif |
| 8531 | + default: |
| 8532 | + BUG(); |
| 8533 | + } |
| 8534 | + |
| 8535 | + if (r) { |
| 8536 | + DSSERR("failed to init display %s\n", dssdev->name); |
| 8537 | + return; |
| 8538 | + } |
| 8539 | + |
| 8540 | + /* create device sysfs files */ |
| 8541 | + i = 0; |
| 8542 | + while ((attr = display_sysfs_attrs[i++]) != NULL) { |
| 8543 | + r = device_create_file(&dssdev->dev, attr); |
| 8544 | + if (r) |
| 8545 | + DSSERR("failed to create sysfs file\n"); |
| 8546 | + } |
| 8547 | + |
| 8548 | + /* create display? sysfs links */ |
| 8549 | + r = sysfs_create_link(&pdev->dev.kobj, &dssdev->dev.kobj, |
| 8550 | + dev_name(&dssdev->dev)); |
| 8551 | + if (r) |
| 8552 | + DSSERR("failed to create sysfs display link\n"); |
| 8553 | +} |
| 8554 | + |
| 8555 | +void dss_uninit_device(struct platform_device *pdev, |
| 8556 | + struct omap_dss_device *dssdev) |
| 8557 | +{ |
| 8558 | + struct device_attribute *attr; |
| 8559 | + int i = 0; |
| 8560 | + |
| 8561 | + sysfs_remove_link(&pdev->dev.kobj, dev_name(&dssdev->dev)); |
| 8562 | + |
| 8563 | + while ((attr = display_sysfs_attrs[i++]) != NULL) |
| 8564 | + device_remove_file(&dssdev->dev, attr); |
| 8565 | + |
| 8566 | + if (dssdev->manager) |
| 8567 | + dssdev->manager->unset_device(dssdev->manager); |
| 8568 | +} |
| 8569 | + |
| 8570 | +static int dss_suspend_device(struct device *dev, void *data) |
| 8571 | +{ |
| 8572 | + int r; |
| 8573 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8574 | + |
| 8575 | + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { |
| 8576 | + dssdev->activate_after_resume = false; |
| 8577 | + return 0; |
| 8578 | + } |
| 8579 | + |
| 8580 | + if (!dssdev->suspend) { |
| 8581 | + DSSERR("display '%s' doesn't implement suspend\n", |
| 8582 | + dssdev->name); |
| 8583 | + return -ENOSYS; |
| 8584 | + } |
| 8585 | + |
| 8586 | + r = dssdev->suspend(dssdev); |
| 8587 | + if (r) |
| 8588 | + return r; |
| 8589 | + |
| 8590 | + dssdev->activate_after_resume = true; |
| 8591 | + |
| 8592 | + return 0; |
| 8593 | +} |
| 8594 | + |
| 8595 | +int dss_suspend_all_devices(void) |
| 8596 | +{ |
| 8597 | + int r; |
| 8598 | + struct bus_type *bus = dss_get_bus(); |
| 8599 | + |
| 8600 | + r = bus_for_each_dev(bus, NULL, NULL, dss_suspend_device); |
| 8601 | + if (r) { |
| 8602 | + /* resume all displays that were suspended */ |
| 8603 | + dss_resume_all_devices(); |
| 8604 | + return r; |
| 8605 | + } |
| 8606 | + |
| 8607 | + return 0; |
| 8608 | +} |
| 8609 | + |
| 8610 | +static int dss_resume_device(struct device *dev, void *data) |
| 8611 | +{ |
| 8612 | + int r; |
| 8613 | + struct omap_dss_device *dssdev = to_dss_device(dev); |
| 8614 | + |
| 8615 | + if (dssdev->activate_after_resume && dssdev->resume) { |
| 8616 | + r = dssdev->resume(dssdev); |
| 8617 | + if (r) |
| 8618 | + return r; |
| 8619 | + } |
| 8620 | + |
| 8621 | + dssdev->activate_after_resume = false; |
| 8622 | + |
| 8623 | + return 0; |
| 8624 | +} |
| 8625 | + |
| 8626 | +int dss_resume_all_devices(void) |
| 8627 | +{ |
| 8628 | + struct bus_type *bus = dss_get_bus(); |
| 8629 | + |
| 8630 | + return bus_for_each_dev(bus, NULL, NULL, dss_resume_device); |
| 8631 | +} |
| 8632 | + |
| 8633 | + |
| 8634 | +void omap_dss_get_device(struct omap_dss_device *dssdev) |
| 8635 | +{ |
| 8636 | + get_device(&dssdev->dev); |
| 8637 | +} |
| 8638 | +EXPORT_SYMBOL(omap_dss_get_device); |
| 8639 | + |
| 8640 | +void omap_dss_put_device(struct omap_dss_device *dssdev) |
| 8641 | +{ |
| 8642 | + put_device(&dssdev->dev); |
| 8643 | +} |
| 8644 | +EXPORT_SYMBOL(omap_dss_put_device); |
| 8645 | + |
| 8646 | +/* ref count of the found device is incremented. ref count |
| 8647 | + * of from-device is decremented. */ |
| 8648 | +struct omap_dss_device *omap_dss_get_next_device(struct omap_dss_device *from) |
| 8649 | +{ |
| 8650 | + struct device *dev; |
| 8651 | + struct device *dev_start = NULL; |
| 8652 | + struct omap_dss_device *dssdev = NULL; |
| 8653 | + |
| 8654 | + int match(struct device *dev, void *data) |
| 8655 | + { |
| 8656 | + /* skip panels connected to controllers */ |
| 8657 | + if (to_dss_device(dev)->panel.ctrl) |
| 8658 | + return 0; |
| 8659 | + |
| 8660 | + return 1; |
| 8661 | + } |
| 8662 | + |
| 8663 | + if (from) |
| 8664 | + dev_start = &from->dev; |
| 8665 | + dev = bus_find_device(dss_get_bus(), dev_start, NULL, match); |
| 8666 | + if (dev) |
| 8667 | + dssdev = to_dss_device(dev); |
| 8668 | + if (from) |
| 8669 | + put_device(&from->dev); |
| 8670 | + |
| 8671 | + return dssdev; |
| 8672 | +} |
| 8673 | +EXPORT_SYMBOL(omap_dss_get_next_device); |
| 8674 | + |
| 8675 | +struct omap_dss_device *omap_dss_find_device(void *data, |
| 8676 | + int (*match)(struct omap_dss_device *dssdev, void *data)) |
| 8677 | +{ |
| 8678 | + struct omap_dss_device *dssdev = NULL; |
| 8679 | + |
| 8680 | + while ((dssdev = omap_dss_get_next_device(dssdev)) != NULL) { |
| 8681 | + if (match(dssdev, data)) |
| 8682 | + return dssdev; |
| 8683 | + } |
| 8684 | + |
| 8685 | + return NULL; |
| 8686 | +} |
| 8687 | +EXPORT_SYMBOL(omap_dss_find_device); |
| 8688 | + |
| 8689 | +int omap_dss_start_device(struct omap_dss_device *dssdev) |
| 8690 | +{ |
| 8691 | + int r; |
| 8692 | + |
| 8693 | + if (!dssdev->driver) { |
| 8694 | + DSSDBG("no driver\n"); |
| 8695 | + r = -ENODEV; |
| 8696 | + goto err0; |
| 8697 | + } |
| 8698 | + |
| 8699 | + if (dssdev->ctrl.panel && !dssdev->ctrl.panel->driver) { |
| 8700 | + DSSDBG("no panel driver\n"); |
| 8701 | + r = -ENODEV; |
| 8702 | + goto err0; |
| 8703 | + } |
| 8704 | + |
| 8705 | + if (!try_module_get(dssdev->dev.driver->owner)) { |
| 8706 | + r = -ENODEV; |
| 8707 | + goto err0; |
| 8708 | + } |
| 8709 | + |
| 8710 | + if (dssdev->ctrl.panel) { |
| 8711 | + if (!try_module_get(dssdev->ctrl.panel->dev.driver->owner)) { |
| 8712 | + r = -ENODEV; |
| 8713 | + goto err1; |
| 8714 | + } |
| 8715 | + } |
| 8716 | + |
| 8717 | + return 0; |
| 8718 | +err1: |
| 8719 | + module_put(dssdev->dev.driver->owner); |
| 8720 | +err0: |
| 8721 | + return r; |
| 8722 | +} |
| 8723 | +EXPORT_SYMBOL(omap_dss_start_device); |
| 8724 | + |
| 8725 | +void omap_dss_stop_device(struct omap_dss_device *dssdev) |
| 8726 | +{ |
| 8727 | + if (dssdev->ctrl.panel) |
| 8728 | + module_put(dssdev->ctrl.panel->dev.driver->owner); |
| 8729 | + |
| 8730 | + module_put(dssdev->dev.driver->owner); |
| 8731 | +} |
| 8732 | +EXPORT_SYMBOL(omap_dss_stop_device); |
| 8733 | + |
| 8734 | --- /dev/null |
| 8735 | +++ b/drivers/video/omap2/dss/dpi.c |
| 8736 | @@ -0,0 +1,388 @@ |
| 8737 | +/* |
| 8738 | + * linux/drivers/video/omap2/dss/dpi.c |
| 8739 | + * |
| 8740 | + * Copyright (C) 2009 Nokia Corporation |
| 8741 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 8742 | + * |
| 8743 | + * Some code and ideas taken from drivers/video/omap/ driver |
| 8744 | + * by Imre Deak. |
| 8745 | + * |
| 8746 | + * This program is free software; you can redistribute it and/or modify it |
| 8747 | + * under the terms of the GNU General Public License version 2 as published by |
| 8748 | + * the Free Software Foundation. |
| 8749 | + * |
| 8750 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 8751 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 8752 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 8753 | + * more details. |
| 8754 | + * |
| 8755 | + * You should have received a copy of the GNU General Public License along with |
| 8756 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 8757 | + */ |
| 8758 | + |
| 8759 | +#define DSS_SUBSYS_NAME "DPI" |
| 8760 | + |
| 8761 | +#include <linux/kernel.h> |
| 8762 | +#include <linux/clk.h> |
| 8763 | +#include <linux/delay.h> |
| 8764 | +#include <linux/errno.h> |
| 8765 | + |
| 8766 | +#include <mach/board.h> |
| 8767 | +#include <mach/display.h> |
| 8768 | +#include <mach/cpu.h> |
| 8769 | + |
| 8770 | +#include "dss.h" |
| 8771 | + |
| 8772 | +static struct { |
| 8773 | + int update_enabled; |
| 8774 | +} dpi; |
| 8775 | + |
| 8776 | +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL |
| 8777 | +static int dpi_set_dsi_clk(bool is_tft, unsigned long pck_req, |
| 8778 | + unsigned long *fck, int *lck_div, int *pck_div) |
| 8779 | +{ |
| 8780 | + struct dsi_clock_info cinfo; |
| 8781 | + int r; |
| 8782 | + |
| 8783 | + r = dsi_pll_calc_pck(is_tft, pck_req, &cinfo); |
| 8784 | + if (r) |
| 8785 | + return r; |
| 8786 | + |
| 8787 | + r = dsi_pll_program(&cinfo); |
| 8788 | + if (r) |
| 8789 | + return r; |
| 8790 | + |
| 8791 | + dss_select_clk_source(0, 1); |
| 8792 | + |
| 8793 | + dispc_set_lcd_divisor(cinfo.lck_div, cinfo.pck_div); |
| 8794 | + |
| 8795 | + *fck = cinfo.dsi1_pll_fclk; |
| 8796 | + *lck_div = cinfo.lck_div; |
| 8797 | + *pck_div = cinfo.pck_div; |
| 8798 | + |
| 8799 | + return 0; |
| 8800 | +} |
| 8801 | +#else |
| 8802 | +static int dpi_set_dispc_clk(bool is_tft, unsigned long pck_req, |
| 8803 | + unsigned long *fck, int *lck_div, int *pck_div) |
| 8804 | +{ |
| 8805 | + struct dispc_clock_info cinfo; |
| 8806 | + int r; |
| 8807 | + |
| 8808 | + r = dispc_calc_clock_div(is_tft, pck_req, &cinfo); |
| 8809 | + if (r) |
| 8810 | + return r; |
| 8811 | + |
| 8812 | + r = dispc_set_clock_div(&cinfo); |
| 8813 | + if (r) |
| 8814 | + return r; |
| 8815 | + |
| 8816 | + *fck = cinfo.fck; |
| 8817 | + *lck_div = cinfo.lck_div; |
| 8818 | + *pck_div = cinfo.pck_div; |
| 8819 | + |
| 8820 | + return 0; |
| 8821 | +} |
| 8822 | +#endif |
| 8823 | + |
| 8824 | +static int dpi_set_mode(struct omap_dss_device *dssdev) |
| 8825 | +{ |
| 8826 | + struct omap_video_timings *t = &dssdev->panel.timings; |
| 8827 | + int lck_div, pck_div; |
| 8828 | + unsigned long fck; |
| 8829 | + unsigned long pck; |
| 8830 | + bool is_tft; |
| 8831 | + int r = 0; |
| 8832 | + |
| 8833 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 8834 | + |
| 8835 | + dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi, |
| 8836 | + dssdev->panel.acb); |
| 8837 | + |
| 8838 | + is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; |
| 8839 | + |
| 8840 | +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL |
| 8841 | + r = dpi_set_dsi_clk(is_tft, t->pixel_clock * 1000, |
| 8842 | + &fck, &lck_div, &pck_div); |
| 8843 | +#else |
| 8844 | + r = dpi_set_dispc_clk(is_tft, t->pixel_clock * 1000, |
| 8845 | + &fck, &lck_div, &pck_div); |
| 8846 | +#endif |
| 8847 | + if (r) |
| 8848 | + goto err0; |
| 8849 | + |
| 8850 | + pck = fck / lck_div / pck_div / 1000; |
| 8851 | + |
| 8852 | + if (pck != t->pixel_clock) { |
| 8853 | + DSSWARN("Could not find exact pixel clock. " |
| 8854 | + "Requested %d kHz, got %lu kHz\n", |
| 8855 | + t->pixel_clock, pck); |
| 8856 | + |
| 8857 | + t->pixel_clock = pck; |
| 8858 | + } |
| 8859 | + |
| 8860 | + dispc_set_lcd_timings(t); |
| 8861 | + |
| 8862 | +err0: |
| 8863 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 8864 | + return r; |
| 8865 | +} |
| 8866 | + |
| 8867 | +static int dpi_basic_init(struct omap_dss_device *dssdev) |
| 8868 | +{ |
| 8869 | + bool is_tft; |
| 8870 | + |
| 8871 | + is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; |
| 8872 | + |
| 8873 | + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS); |
| 8874 | + dispc_set_lcd_display_type(is_tft ? OMAP_DSS_LCD_DISPLAY_TFT : |
| 8875 | + OMAP_DSS_LCD_DISPLAY_STN); |
| 8876 | + dispc_set_tft_data_lines(dssdev->phy.dpi.data_lines); |
| 8877 | + |
| 8878 | + return 0; |
| 8879 | +} |
| 8880 | + |
| 8881 | +static int dpi_display_enable(struct omap_dss_device *dssdev) |
| 8882 | +{ |
| 8883 | + int r; |
| 8884 | + |
| 8885 | + r = omap_dss_start_device(dssdev); |
| 8886 | + if (r) { |
| 8887 | + DSSERR("failed to start device\n"); |
| 8888 | + goto err0; |
| 8889 | + } |
| 8890 | + |
| 8891 | + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { |
| 8892 | + DSSERR("display already enabled\n"); |
| 8893 | + r = -EINVAL; |
| 8894 | + goto err1; |
| 8895 | + } |
| 8896 | + |
| 8897 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 8898 | + |
| 8899 | + r = dpi_basic_init(dssdev); |
| 8900 | + if (r) |
| 8901 | + goto err2; |
| 8902 | + |
| 8903 | +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL |
| 8904 | + dss_clk_enable(DSS_CLK_FCK2); |
| 8905 | + r = dsi_pll_init(0, 1); |
| 8906 | + if (r) |
| 8907 | + goto err3; |
| 8908 | +#endif |
| 8909 | + r = dpi_set_mode(dssdev); |
| 8910 | + if (r) |
| 8911 | + goto err4; |
| 8912 | + |
| 8913 | + mdelay(2); |
| 8914 | + |
| 8915 | + dispc_enable_lcd_out(1); |
| 8916 | + |
| 8917 | + r = dssdev->driver->enable(dssdev); |
| 8918 | + if (r) |
| 8919 | + goto err5; |
| 8920 | + |
| 8921 | + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; |
| 8922 | + |
| 8923 | + return 0; |
| 8924 | + |
| 8925 | +err5: |
| 8926 | + dispc_enable_lcd_out(0); |
| 8927 | +err4: |
| 8928 | +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL |
| 8929 | + dsi_pll_uninit(); |
| 8930 | +err3: |
| 8931 | + dss_clk_disable(DSS_CLK_FCK2); |
| 8932 | +#endif |
| 8933 | +err2: |
| 8934 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 8935 | +err1: |
| 8936 | + omap_dss_stop_device(dssdev); |
| 8937 | +err0: |
| 8938 | + return r; |
| 8939 | +} |
| 8940 | + |
| 8941 | +static int dpi_display_resume(struct omap_dss_device *dssdev); |
| 8942 | + |
| 8943 | +static void dpi_display_disable(struct omap_dss_device *dssdev) |
| 8944 | +{ |
| 8945 | + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) |
| 8946 | + return; |
| 8947 | + |
| 8948 | + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) |
| 8949 | + dpi_display_resume(dssdev); |
| 8950 | + |
| 8951 | + dssdev->driver->disable(dssdev); |
| 8952 | + |
| 8953 | + dispc_enable_lcd_out(0); |
| 8954 | + |
| 8955 | +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL |
| 8956 | + dss_select_clk_source(0, 0); |
| 8957 | + dsi_pll_uninit(); |
| 8958 | + dss_clk_disable(DSS_CLK_FCK2); |
| 8959 | +#endif |
| 8960 | + |
| 8961 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 8962 | + |
| 8963 | + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; |
| 8964 | + |
| 8965 | + omap_dss_stop_device(dssdev); |
| 8966 | +} |
| 8967 | + |
| 8968 | +static int dpi_display_suspend(struct omap_dss_device *dssdev) |
| 8969 | +{ |
| 8970 | + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) |
| 8971 | + return -EINVAL; |
| 8972 | + |
| 8973 | + DSSDBG("dpi_display_suspend\n"); |
| 8974 | + |
| 8975 | + if (dssdev->driver->suspend) |
| 8976 | + dssdev->driver->suspend(dssdev); |
| 8977 | + |
| 8978 | + dispc_enable_lcd_out(0); |
| 8979 | + |
| 8980 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 8981 | + |
| 8982 | + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; |
| 8983 | + |
| 8984 | + return 0; |
| 8985 | +} |
| 8986 | + |
| 8987 | +static int dpi_display_resume(struct omap_dss_device *dssdev) |
| 8988 | +{ |
| 8989 | + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) |
| 8990 | + return -EINVAL; |
| 8991 | + |
| 8992 | + DSSDBG("dpi_display_resume\n"); |
| 8993 | + |
| 8994 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 8995 | + |
| 8996 | + dispc_enable_lcd_out(1); |
| 8997 | + |
| 8998 | + if (dssdev->driver->resume) |
| 8999 | + dssdev->driver->resume(dssdev); |
| 9000 | + |
| 9001 | + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; |
| 9002 | + |
| 9003 | + return 0; |
| 9004 | +} |
| 9005 | + |
| 9006 | +static void dpi_set_timings(struct omap_dss_device *dssdev, |
| 9007 | + struct omap_video_timings *timings) |
| 9008 | +{ |
| 9009 | + DSSDBG("dpi_set_timings\n"); |
| 9010 | + dssdev->panel.timings = *timings; |
| 9011 | + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { |
| 9012 | + dpi_set_mode(dssdev); |
| 9013 | + dispc_go(OMAP_DSS_CHANNEL_LCD); |
| 9014 | + } |
| 9015 | +} |
| 9016 | + |
| 9017 | +static int dpi_check_timings(struct omap_dss_device *dssdev, |
| 9018 | + struct omap_video_timings *timings) |
| 9019 | +{ |
| 9020 | + bool is_tft; |
| 9021 | + int r; |
| 9022 | + int lck_div, pck_div; |
| 9023 | + unsigned long fck; |
| 9024 | + unsigned long pck; |
| 9025 | + |
| 9026 | + if (!dispc_lcd_timings_ok(timings)) |
| 9027 | + return -EINVAL; |
| 9028 | + |
| 9029 | + if (timings->pixel_clock == 0) |
| 9030 | + return -EINVAL; |
| 9031 | + |
| 9032 | + is_tft = (dssdev->panel.config & OMAP_DSS_LCD_TFT) != 0; |
| 9033 | + |
| 9034 | +#ifdef CONFIG_OMAP2_DSS_USE_DSI_PLL |
| 9035 | + { |
| 9036 | + struct dsi_clock_info cinfo; |
| 9037 | + r = dsi_pll_calc_pck(is_tft, timings->pixel_clock * 1000, |
| 9038 | + &cinfo); |
| 9039 | + |
| 9040 | + if (r) |
| 9041 | + return r; |
| 9042 | + |
| 9043 | + fck = cinfo.dsi1_pll_fclk; |
| 9044 | + lck_div = cinfo.lck_div; |
| 9045 | + pck_div = cinfo.pck_div; |
| 9046 | + } |
| 9047 | +#else |
| 9048 | + { |
| 9049 | + struct dispc_clock_info cinfo; |
| 9050 | + r = dispc_calc_clock_div(is_tft, timings->pixel_clock * 1000, |
| 9051 | + &cinfo); |
| 9052 | + |
| 9053 | + if (r) |
| 9054 | + return r; |
| 9055 | + |
| 9056 | + fck = cinfo.fck; |
| 9057 | + lck_div = cinfo.lck_div; |
| 9058 | + pck_div = cinfo.pck_div; |
| 9059 | + } |
| 9060 | +#endif |
| 9061 | + |
| 9062 | + pck = fck / lck_div / pck_div / 1000; |
| 9063 | + |
| 9064 | + timings->pixel_clock = pck; |
| 9065 | + |
| 9066 | + return 0; |
| 9067 | +} |
| 9068 | + |
| 9069 | +static void dpi_get_timings(struct omap_dss_device *dssdev, |
| 9070 | + struct omap_video_timings *timings) |
| 9071 | +{ |
| 9072 | + *timings = dssdev->panel.timings; |
| 9073 | +} |
| 9074 | + |
| 9075 | +static int dpi_display_set_update_mode(struct omap_dss_device *dssdev, |
| 9076 | + enum omap_dss_update_mode mode) |
| 9077 | +{ |
| 9078 | + if (mode == OMAP_DSS_UPDATE_MANUAL) |
| 9079 | + return -EINVAL; |
| 9080 | + |
| 9081 | + if (mode == OMAP_DSS_UPDATE_DISABLED) { |
| 9082 | + dispc_enable_lcd_out(0); |
| 9083 | + dpi.update_enabled = 0; |
| 9084 | + } else { |
| 9085 | + dispc_enable_lcd_out(1); |
| 9086 | + dpi.update_enabled = 1; |
| 9087 | + } |
| 9088 | + |
| 9089 | + return 0; |
| 9090 | +} |
| 9091 | + |
| 9092 | +static enum omap_dss_update_mode dpi_display_get_update_mode( |
| 9093 | + struct omap_dss_device *dssdev) |
| 9094 | +{ |
| 9095 | + return dpi.update_enabled ? OMAP_DSS_UPDATE_AUTO : |
| 9096 | + OMAP_DSS_UPDATE_DISABLED; |
| 9097 | +} |
| 9098 | + |
| 9099 | +int dpi_init_display(struct omap_dss_device *dssdev) |
| 9100 | +{ |
| 9101 | + DSSDBG("init_display\n"); |
| 9102 | + |
| 9103 | + dssdev->enable = dpi_display_enable; |
| 9104 | + dssdev->disable = dpi_display_disable; |
| 9105 | + dssdev->suspend = dpi_display_suspend; |
| 9106 | + dssdev->resume = dpi_display_resume; |
| 9107 | + dssdev->set_timings = dpi_set_timings; |
| 9108 | + dssdev->check_timings = dpi_check_timings; |
| 9109 | + dssdev->get_timings = dpi_get_timings; |
| 9110 | + dssdev->set_update_mode = dpi_display_set_update_mode; |
| 9111 | + dssdev->get_update_mode = dpi_display_get_update_mode; |
| 9112 | + |
| 9113 | + return 0; |
| 9114 | +} |
| 9115 | + |
| 9116 | +int dpi_init(void) |
| 9117 | +{ |
| 9118 | + return 0; |
| 9119 | +} |
| 9120 | + |
| 9121 | +void dpi_exit(void) |
| 9122 | +{ |
| 9123 | +} |
| 9124 | + |
| 9125 | --- /dev/null |
| 9126 | +++ b/drivers/video/omap2/dss/dsi.c |
| 9127 | @@ -0,0 +1,3509 @@ |
| 9128 | +/* |
| 9129 | + * linux/drivers/video/omap2/dss/dsi.c |
| 9130 | + * |
| 9131 | + * Copyright (C) 2009 Nokia Corporation |
| 9132 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 9133 | + * |
| 9134 | + * This program is free software; you can redistribute it and/or modify it |
| 9135 | + * under the terms of the GNU General Public License version 2 as published by |
| 9136 | + * the Free Software Foundation. |
| 9137 | + * |
| 9138 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 9139 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 9140 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 9141 | + * more details. |
| 9142 | + * |
| 9143 | + * You should have received a copy of the GNU General Public License along with |
| 9144 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 9145 | + */ |
| 9146 | + |
| 9147 | +#define DSS_SUBSYS_NAME "DSI" |
| 9148 | + |
| 9149 | +#include <linux/kernel.h> |
| 9150 | +#include <linux/io.h> |
| 9151 | +#include <linux/clk.h> |
| 9152 | +#include <linux/device.h> |
| 9153 | +#include <linux/err.h> |
| 9154 | +#include <linux/interrupt.h> |
| 9155 | +#include <linux/delay.h> |
| 9156 | +#include <linux/mutex.h> |
| 9157 | +#include <linux/seq_file.h> |
| 9158 | +#include <linux/platform_device.h> |
| 9159 | +#include <linux/regulator/consumer.h> |
| 9160 | +#include <linux/kthread.h> |
| 9161 | +#include <linux/wait.h> |
| 9162 | + |
| 9163 | +#include <mach/board.h> |
| 9164 | +#include <mach/display.h> |
| 9165 | +#include <mach/clock.h> |
| 9166 | + |
| 9167 | +#include "dss.h" |
| 9168 | + |
| 9169 | +/*#define VERBOSE_IRQ*/ |
| 9170 | + |
| 9171 | +#define DSI_BASE 0x4804FC00 |
| 9172 | + |
| 9173 | +struct dsi_reg { u16 idx; }; |
| 9174 | + |
| 9175 | +#define DSI_REG(idx) ((const struct dsi_reg) { idx }) |
| 9176 | + |
| 9177 | +#define DSI_SZ_REGS SZ_1K |
| 9178 | +/* DSI Protocol Engine */ |
| 9179 | + |
| 9180 | +#define DSI_REVISION DSI_REG(0x0000) |
| 9181 | +#define DSI_SYSCONFIG DSI_REG(0x0010) |
| 9182 | +#define DSI_SYSSTATUS DSI_REG(0x0014) |
| 9183 | +#define DSI_IRQSTATUS DSI_REG(0x0018) |
| 9184 | +#define DSI_IRQENABLE DSI_REG(0x001C) |
| 9185 | +#define DSI_CTRL DSI_REG(0x0040) |
| 9186 | +#define DSI_COMPLEXIO_CFG1 DSI_REG(0x0048) |
| 9187 | +#define DSI_COMPLEXIO_IRQ_STATUS DSI_REG(0x004C) |
| 9188 | +#define DSI_COMPLEXIO_IRQ_ENABLE DSI_REG(0x0050) |
| 9189 | +#define DSI_CLK_CTRL DSI_REG(0x0054) |
| 9190 | +#define DSI_TIMING1 DSI_REG(0x0058) |
| 9191 | +#define DSI_TIMING2 DSI_REG(0x005C) |
| 9192 | +#define DSI_VM_TIMING1 DSI_REG(0x0060) |
| 9193 | +#define DSI_VM_TIMING2 DSI_REG(0x0064) |
| 9194 | +#define DSI_VM_TIMING3 DSI_REG(0x0068) |
| 9195 | +#define DSI_CLK_TIMING DSI_REG(0x006C) |
| 9196 | +#define DSI_TX_FIFO_VC_SIZE DSI_REG(0x0070) |
| 9197 | +#define DSI_RX_FIFO_VC_SIZE DSI_REG(0x0074) |
| 9198 | +#define DSI_COMPLEXIO_CFG2 DSI_REG(0x0078) |
| 9199 | +#define DSI_RX_FIFO_VC_FULLNESS DSI_REG(0x007C) |
| 9200 | +#define DSI_VM_TIMING4 DSI_REG(0x0080) |
| 9201 | +#define DSI_TX_FIFO_VC_EMPTINESS DSI_REG(0x0084) |
| 9202 | +#define DSI_VM_TIMING5 DSI_REG(0x0088) |
| 9203 | +#define DSI_VM_TIMING6 DSI_REG(0x008C) |
| 9204 | +#define DSI_VM_TIMING7 DSI_REG(0x0090) |
| 9205 | +#define DSI_STOPCLK_TIMING DSI_REG(0x0094) |
| 9206 | +#define DSI_VC_CTRL(n) DSI_REG(0x0100 + (n * 0x20)) |
| 9207 | +#define DSI_VC_TE(n) DSI_REG(0x0104 + (n * 0x20)) |
| 9208 | +#define DSI_VC_LONG_PACKET_HEADER(n) DSI_REG(0x0108 + (n * 0x20)) |
| 9209 | +#define DSI_VC_LONG_PACKET_PAYLOAD(n) DSI_REG(0x010C + (n * 0x20)) |
| 9210 | +#define DSI_VC_SHORT_PACKET_HEADER(n) DSI_REG(0x0110 + (n * 0x20)) |
| 9211 | +#define DSI_VC_IRQSTATUS(n) DSI_REG(0x0118 + (n * 0x20)) |
| 9212 | +#define DSI_VC_IRQENABLE(n) DSI_REG(0x011C + (n * 0x20)) |
| 9213 | + |
| 9214 | +/* DSIPHY_SCP */ |
| 9215 | + |
| 9216 | +#define DSI_DSIPHY_CFG0 DSI_REG(0x200 + 0x0000) |
| 9217 | +#define DSI_DSIPHY_CFG1 DSI_REG(0x200 + 0x0004) |
| 9218 | +#define DSI_DSIPHY_CFG2 DSI_REG(0x200 + 0x0008) |
| 9219 | +#define DSI_DSIPHY_CFG5 DSI_REG(0x200 + 0x0014) |
| 9220 | + |
| 9221 | +/* DSI_PLL_CTRL_SCP */ |
| 9222 | + |
| 9223 | +#define DSI_PLL_CONTROL DSI_REG(0x300 + 0x0000) |
| 9224 | +#define DSI_PLL_STATUS DSI_REG(0x300 + 0x0004) |
| 9225 | +#define DSI_PLL_GO DSI_REG(0x300 + 0x0008) |
| 9226 | +#define DSI_PLL_CONFIGURATION1 DSI_REG(0x300 + 0x000C) |
| 9227 | +#define DSI_PLL_CONFIGURATION2 DSI_REG(0x300 + 0x0010) |
| 9228 | + |
| 9229 | +#define REG_GET(idx, start, end) \ |
| 9230 | + FLD_GET(dsi_read_reg(idx), start, end) |
| 9231 | + |
| 9232 | +#define REG_FLD_MOD(idx, val, start, end) \ |
| 9233 | + dsi_write_reg(idx, FLD_MOD(dsi_read_reg(idx), val, start, end)) |
| 9234 | + |
| 9235 | +/* Global interrupts */ |
| 9236 | +#define DSI_IRQ_VC0 (1 << 0) |
| 9237 | +#define DSI_IRQ_VC1 (1 << 1) |
| 9238 | +#define DSI_IRQ_VC2 (1 << 2) |
| 9239 | +#define DSI_IRQ_VC3 (1 << 3) |
| 9240 | +#define DSI_IRQ_WAKEUP (1 << 4) |
| 9241 | +#define DSI_IRQ_RESYNC (1 << 5) |
| 9242 | +#define DSI_IRQ_PLL_LOCK (1 << 7) |
| 9243 | +#define DSI_IRQ_PLL_UNLOCK (1 << 8) |
| 9244 | +#define DSI_IRQ_PLL_RECALL (1 << 9) |
| 9245 | +#define DSI_IRQ_COMPLEXIO_ERR (1 << 10) |
| 9246 | +#define DSI_IRQ_HS_TX_TIMEOUT (1 << 14) |
| 9247 | +#define DSI_IRQ_LP_RX_TIMEOUT (1 << 15) |
| 9248 | +#define DSI_IRQ_TE_TRIGGER (1 << 16) |
| 9249 | +#define DSI_IRQ_ACK_TRIGGER (1 << 17) |
| 9250 | +#define DSI_IRQ_SYNC_LOST (1 << 18) |
| 9251 | +#define DSI_IRQ_LDO_POWER_GOOD (1 << 19) |
| 9252 | +#define DSI_IRQ_TA_TIMEOUT (1 << 20) |
| 9253 | +#define DSI_IRQ_ERROR_MASK \ |
| 9254 | + (DSI_IRQ_HS_TX_TIMEOUT | DSI_IRQ_LP_RX_TIMEOUT | DSI_IRQ_SYNC_LOST | \ |
| 9255 | + DSI_IRQ_TA_TIMEOUT) |
| 9256 | +#define DSI_IRQ_CHANNEL_MASK 0xf |
| 9257 | + |
| 9258 | +/* Virtual channel interrupts */ |
| 9259 | +#define DSI_VC_IRQ_CS (1 << 0) |
| 9260 | +#define DSI_VC_IRQ_ECC_CORR (1 << 1) |
| 9261 | +#define DSI_VC_IRQ_PACKET_SENT (1 << 2) |
| 9262 | +#define DSI_VC_IRQ_FIFO_TX_OVF (1 << 3) |
| 9263 | +#define DSI_VC_IRQ_FIFO_RX_OVF (1 << 4) |
| 9264 | +#define DSI_VC_IRQ_BTA (1 << 5) |
| 9265 | +#define DSI_VC_IRQ_ECC_NO_CORR (1 << 6) |
| 9266 | +#define DSI_VC_IRQ_FIFO_TX_UDF (1 << 7) |
| 9267 | +#define DSI_VC_IRQ_PP_BUSY_CHANGE (1 << 8) |
| 9268 | +#define DSI_VC_IRQ_ERROR_MASK \ |
| 9269 | + (DSI_VC_IRQ_CS | DSI_VC_IRQ_ECC_CORR | DSI_VC_IRQ_FIFO_TX_OVF | \ |
| 9270 | + DSI_VC_IRQ_FIFO_RX_OVF | DSI_VC_IRQ_ECC_NO_CORR | \ |
| 9271 | + DSI_VC_IRQ_FIFO_TX_UDF) |
| 9272 | + |
| 9273 | +/* ComplexIO interrupts */ |
| 9274 | +#define DSI_CIO_IRQ_ERRSYNCESC1 (1 << 0) |
| 9275 | +#define DSI_CIO_IRQ_ERRSYNCESC2 (1 << 1) |
| 9276 | +#define DSI_CIO_IRQ_ERRSYNCESC3 (1 << 2) |
| 9277 | +#define DSI_CIO_IRQ_ERRESC1 (1 << 5) |
| 9278 | +#define DSI_CIO_IRQ_ERRESC2 (1 << 6) |
| 9279 | +#define DSI_CIO_IRQ_ERRESC3 (1 << 7) |
| 9280 | +#define DSI_CIO_IRQ_ERRCONTROL1 (1 << 10) |
| 9281 | +#define DSI_CIO_IRQ_ERRCONTROL2 (1 << 11) |
| 9282 | +#define DSI_CIO_IRQ_ERRCONTROL3 (1 << 12) |
| 9283 | +#define DSI_CIO_IRQ_STATEULPS1 (1 << 15) |
| 9284 | +#define DSI_CIO_IRQ_STATEULPS2 (1 << 16) |
| 9285 | +#define DSI_CIO_IRQ_STATEULPS3 (1 << 17) |
| 9286 | +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_1 (1 << 20) |
| 9287 | +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_1 (1 << 21) |
| 9288 | +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_2 (1 << 22) |
| 9289 | +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_2 (1 << 23) |
| 9290 | +#define DSI_CIO_IRQ_ERRCONTENTIONLP0_3 (1 << 24) |
| 9291 | +#define DSI_CIO_IRQ_ERRCONTENTIONLP1_3 (1 << 25) |
| 9292 | +#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL0 (1 << 30) |
| 9293 | +#define DSI_CIO_IRQ_ULPSACTIVENOT_ALL1 (1 << 31) |
| 9294 | + |
| 9295 | +#define DSI_DT_DCS_SHORT_WRITE_0 0x05 |
| 9296 | +#define DSI_DT_DCS_SHORT_WRITE_1 0x15 |
| 9297 | +#define DSI_DT_DCS_READ 0x06 |
| 9298 | +#define DSI_DT_SET_MAX_RET_PKG_SIZE 0x37 |
| 9299 | +#define DSI_DT_NULL_PACKET 0x09 |
| 9300 | +#define DSI_DT_DCS_LONG_WRITE 0x39 |
| 9301 | + |
| 9302 | +#define DSI_DT_RX_ACK_WITH_ERR 0x02 |
| 9303 | +#define DSI_DT_RX_DCS_LONG_READ 0x1c |
| 9304 | +#define DSI_DT_RX_SHORT_READ_1 0x21 |
| 9305 | +#define DSI_DT_RX_SHORT_READ_2 0x22 |
| 9306 | + |
| 9307 | +#define FINT_MAX 2100000 |
| 9308 | +#define FINT_MIN 750000 |
| 9309 | +#define REGN_MAX (1 << 7) |
| 9310 | +#define REGM_MAX ((1 << 11) - 1) |
| 9311 | +#define REGM3_MAX (1 << 4) |
| 9312 | +#define REGM4_MAX (1 << 4) |
| 9313 | + |
| 9314 | +enum fifo_size { |
| 9315 | + DSI_FIFO_SIZE_0 = 0, |
| 9316 | + DSI_FIFO_SIZE_32 = 1, |
| 9317 | + DSI_FIFO_SIZE_64 = 2, |
| 9318 | + DSI_FIFO_SIZE_96 = 3, |
| 9319 | + DSI_FIFO_SIZE_128 = 4, |
| 9320 | +}; |
| 9321 | + |
| 9322 | +enum dsi_vc_mode { |
| 9323 | + DSI_VC_MODE_L4 = 0, |
| 9324 | + DSI_VC_MODE_VP, |
| 9325 | +}; |
| 9326 | + |
| 9327 | +struct dsi_update_region { |
| 9328 | + bool dirty; |
| 9329 | + u16 x, y, w, h; |
| 9330 | + struct omap_dss_device *device; |
| 9331 | +}; |
| 9332 | + |
| 9333 | +static struct |
| 9334 | +{ |
| 9335 | + void __iomem *base; |
| 9336 | + |
| 9337 | + unsigned long dsi1_pll_fclk; /* Hz */ |
| 9338 | + unsigned long dsi2_pll_fclk; /* Hz */ |
| 9339 | + unsigned long dsiphy; /* Hz */ |
| 9340 | + unsigned long ddr_clk; /* Hz */ |
| 9341 | + |
| 9342 | + struct regulator *vdds_dsi_reg; |
| 9343 | + |
| 9344 | + struct { |
| 9345 | + enum dsi_vc_mode mode; |
| 9346 | + struct omap_dss_device *dssdev; |
| 9347 | + enum fifo_size fifo_size; |
| 9348 | + int dest_per; /* destination peripheral 0-3 */ |
| 9349 | + } vc[4]; |
| 9350 | + |
| 9351 | + struct mutex lock; |
| 9352 | + struct mutex bus_lock; |
| 9353 | + |
| 9354 | + unsigned pll_locked; |
| 9355 | + |
| 9356 | + struct completion bta_completion; |
| 9357 | + |
| 9358 | + struct task_struct *thread; |
| 9359 | + wait_queue_head_t waitqueue; |
| 9360 | + |
| 9361 | + spinlock_t update_lock; |
| 9362 | + bool framedone_received; |
| 9363 | + struct dsi_update_region update_region; |
| 9364 | + struct dsi_update_region active_update_region; |
| 9365 | + struct completion update_completion; |
| 9366 | + |
| 9367 | + enum omap_dss_update_mode user_update_mode; |
| 9368 | + enum omap_dss_update_mode update_mode; |
| 9369 | + bool te_enabled; |
| 9370 | + bool use_ext_te; |
| 9371 | + |
| 9372 | + unsigned long cache_req_pck; |
| 9373 | + unsigned long cache_clk_freq; |
| 9374 | + struct dsi_clock_info cache_cinfo; |
| 9375 | + |
| 9376 | + u32 errors; |
| 9377 | + spinlock_t errors_lock; |
| 9378 | +#ifdef DEBUG |
| 9379 | + ktime_t perf_setup_time; |
| 9380 | + ktime_t perf_start_time; |
| 9381 | + ktime_t perf_start_time_auto; |
| 9382 | + int perf_measure_frames; |
| 9383 | +#endif |
| 9384 | + int debug_read; |
| 9385 | + int debug_write; |
| 9386 | +} dsi; |
| 9387 | + |
| 9388 | +#ifdef DEBUG |
| 9389 | +static unsigned int dsi_perf; |
| 9390 | +module_param_named(dsi_perf, dsi_perf, bool, 0644); |
| 9391 | +#endif |
| 9392 | + |
| 9393 | +static inline void dsi_write_reg(const struct dsi_reg idx, u32 val) |
| 9394 | +{ |
| 9395 | + __raw_writel(val, dsi.base + idx.idx); |
| 9396 | +} |
| 9397 | + |
| 9398 | +static inline u32 dsi_read_reg(const struct dsi_reg idx) |
| 9399 | +{ |
| 9400 | + return __raw_readl(dsi.base + idx.idx); |
| 9401 | +} |
| 9402 | + |
| 9403 | + |
| 9404 | +void dsi_save_context(void) |
| 9405 | +{ |
| 9406 | +} |
| 9407 | + |
| 9408 | +void dsi_restore_context(void) |
| 9409 | +{ |
| 9410 | +} |
| 9411 | + |
| 9412 | +void dsi_bus_lock(void) |
| 9413 | +{ |
| 9414 | + mutex_lock(&dsi.bus_lock); |
| 9415 | +} |
| 9416 | +EXPORT_SYMBOL(dsi_bus_lock); |
| 9417 | + |
| 9418 | +void dsi_bus_unlock(void) |
| 9419 | +{ |
| 9420 | + mutex_unlock(&dsi.bus_lock); |
| 9421 | +} |
| 9422 | +EXPORT_SYMBOL(dsi_bus_unlock); |
| 9423 | + |
| 9424 | +static inline int wait_for_bit_change(const struct dsi_reg idx, int bitnum, |
| 9425 | + int value) |
| 9426 | +{ |
| 9427 | + int t = 100000; |
| 9428 | + |
| 9429 | + while (REG_GET(idx, bitnum, bitnum) != value) { |
| 9430 | + if (--t == 0) |
| 9431 | + return !value; |
| 9432 | + } |
| 9433 | + |
| 9434 | + return value; |
| 9435 | +} |
| 9436 | + |
| 9437 | +#ifdef DEBUG |
| 9438 | +static void dsi_perf_mark_setup(void) |
| 9439 | +{ |
| 9440 | + dsi.perf_setup_time = ktime_get(); |
| 9441 | +} |
| 9442 | + |
| 9443 | +static void dsi_perf_mark_start(void) |
| 9444 | +{ |
| 9445 | + dsi.perf_start_time = ktime_get(); |
| 9446 | +} |
| 9447 | + |
| 9448 | +static void dsi_perf_mark_start_auto(void) |
| 9449 | +{ |
| 9450 | + dsi.perf_measure_frames = 0; |
| 9451 | + dsi.perf_start_time_auto = ktime_get(); |
| 9452 | +} |
| 9453 | + |
| 9454 | +static void dsi_perf_show(const char *name) |
| 9455 | +{ |
| 9456 | + ktime_t t, setup_time, trans_time; |
| 9457 | + u32 total_bytes; |
| 9458 | + u32 setup_us, trans_us, total_us; |
| 9459 | + |
| 9460 | + if (!dsi_perf) |
| 9461 | + return; |
| 9462 | + |
| 9463 | + if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED) |
| 9464 | + return; |
| 9465 | + |
| 9466 | + t = ktime_get(); |
| 9467 | + |
| 9468 | + setup_time = ktime_sub(dsi.perf_start_time, dsi.perf_setup_time); |
| 9469 | + setup_us = (u32)ktime_to_us(setup_time); |
| 9470 | + if (setup_us == 0) |
| 9471 | + setup_us = 1; |
| 9472 | + |
| 9473 | + trans_time = ktime_sub(t, dsi.perf_start_time); |
| 9474 | + trans_us = (u32)ktime_to_us(trans_time); |
| 9475 | + if (trans_us == 0) |
| 9476 | + trans_us = 1; |
| 9477 | + |
| 9478 | + total_us = setup_us + trans_us; |
| 9479 | + |
| 9480 | + total_bytes = dsi.active_update_region.w * |
| 9481 | + dsi.active_update_region.h * |
| 9482 | + dsi.active_update_region.device->ctrl.pixel_size / 8; |
| 9483 | + |
| 9484 | + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) { |
| 9485 | + static u32 s_total_trans_us, s_total_setup_us; |
| 9486 | + static u32 s_min_trans_us = 0xffffffff, s_min_setup_us; |
| 9487 | + static u32 s_max_trans_us, s_max_setup_us; |
| 9488 | + const int numframes = 100; |
| 9489 | + ktime_t total_time_auto; |
| 9490 | + u32 total_time_auto_us; |
| 9491 | + |
| 9492 | + dsi.perf_measure_frames++; |
| 9493 | + |
| 9494 | + if (setup_us < s_min_setup_us) |
| 9495 | + s_min_setup_us = setup_us; |
| 9496 | + |
| 9497 | + if (setup_us > s_max_setup_us) |
| 9498 | + s_max_setup_us = setup_us; |
| 9499 | + |
| 9500 | + s_total_setup_us += setup_us; |
| 9501 | + |
| 9502 | + if (trans_us < s_min_trans_us) |
| 9503 | + s_min_trans_us = trans_us; |
| 9504 | + |
| 9505 | + if (trans_us > s_max_trans_us) |
| 9506 | + s_max_trans_us = trans_us; |
| 9507 | + |
| 9508 | + s_total_trans_us += trans_us; |
| 9509 | + |
| 9510 | + if (dsi.perf_measure_frames < numframes) |
| 9511 | + return; |
| 9512 | + |
| 9513 | + total_time_auto = ktime_sub(t, dsi.perf_start_time_auto); |
| 9514 | + total_time_auto_us = (u32)ktime_to_us(total_time_auto); |
| 9515 | + |
| 9516 | + printk(KERN_INFO "DSI(%s): %u fps, setup %u/%u/%u, " |
| 9517 | + "trans %u/%u/%u\n", |
| 9518 | + name, |
| 9519 | + 1000 * 1000 * numframes / total_time_auto_us, |
| 9520 | + s_min_setup_us, |
| 9521 | + s_max_setup_us, |
| 9522 | + s_total_setup_us / numframes, |
| 9523 | + s_min_trans_us, |
| 9524 | + s_max_trans_us, |
| 9525 | + s_total_trans_us / numframes); |
| 9526 | + |
| 9527 | + s_total_setup_us = 0; |
| 9528 | + s_min_setup_us = 0xffffffff; |
| 9529 | + s_max_setup_us = 0; |
| 9530 | + s_total_trans_us = 0; |
| 9531 | + s_min_trans_us = 0xffffffff; |
| 9532 | + s_max_trans_us = 0; |
| 9533 | + dsi_perf_mark_start_auto(); |
| 9534 | + } else { |
| 9535 | + printk(KERN_INFO "DSI(%s): %u us + %u us = %u us (%uHz), " |
| 9536 | + "%u bytes, %u kbytes/sec\n", |
| 9537 | + name, |
| 9538 | + setup_us, |
| 9539 | + trans_us, |
| 9540 | + total_us, |
| 9541 | + 1000*1000 / total_us, |
| 9542 | + total_bytes, |
| 9543 | + total_bytes * 1000 / total_us); |
| 9544 | + } |
| 9545 | +} |
| 9546 | +#else |
| 9547 | +#define dsi_perf_mark_setup() |
| 9548 | +#define dsi_perf_mark_start() |
| 9549 | +#define dsi_perf_mark_start_auto() |
| 9550 | +#define dsi_perf_show(x) |
| 9551 | +#endif |
| 9552 | + |
| 9553 | +static void print_irq_status(u32 status) |
| 9554 | +{ |
| 9555 | +#ifndef VERBOSE_IRQ |
| 9556 | + if ((status & ~DSI_IRQ_CHANNEL_MASK) == 0) |
| 9557 | + return; |
| 9558 | +#endif |
| 9559 | + printk(KERN_DEBUG "DSI IRQ: 0x%x: ", status); |
| 9560 | + |
| 9561 | +#define PIS(x) \ |
| 9562 | + if (status & DSI_IRQ_##x) \ |
| 9563 | + printk(#x " "); |
| 9564 | +#ifdef VERBOSE_IRQ |
| 9565 | + PIS(VC0); |
| 9566 | + PIS(VC1); |
| 9567 | + PIS(VC2); |
| 9568 | + PIS(VC3); |
| 9569 | +#endif |
| 9570 | + PIS(WAKEUP); |
| 9571 | + PIS(RESYNC); |
| 9572 | + PIS(PLL_LOCK); |
| 9573 | + PIS(PLL_UNLOCK); |
| 9574 | + PIS(PLL_RECALL); |
| 9575 | + PIS(COMPLEXIO_ERR); |
| 9576 | + PIS(HS_TX_TIMEOUT); |
| 9577 | + PIS(LP_RX_TIMEOUT); |
| 9578 | + PIS(TE_TRIGGER); |
| 9579 | + PIS(ACK_TRIGGER); |
| 9580 | + PIS(SYNC_LOST); |
| 9581 | + PIS(LDO_POWER_GOOD); |
| 9582 | + PIS(TA_TIMEOUT); |
| 9583 | +#undef PIS |
| 9584 | + |
| 9585 | + printk("\n"); |
| 9586 | +} |
| 9587 | + |
| 9588 | +static void print_irq_status_vc(int channel, u32 status) |
| 9589 | +{ |
| 9590 | +#ifndef VERBOSE_IRQ |
| 9591 | + if ((status & ~DSI_VC_IRQ_PACKET_SENT) == 0) |
| 9592 | + return; |
| 9593 | +#endif |
| 9594 | + printk(KERN_DEBUG "DSI VC(%d) IRQ 0x%x: ", channel, status); |
| 9595 | + |
| 9596 | +#define PIS(x) \ |
| 9597 | + if (status & DSI_VC_IRQ_##x) \ |
| 9598 | + printk(#x " "); |
| 9599 | + PIS(CS); |
| 9600 | + PIS(ECC_CORR); |
| 9601 | +#ifdef VERBOSE_IRQ |
| 9602 | + PIS(PACKET_SENT); |
| 9603 | +#endif |
| 9604 | + PIS(FIFO_TX_OVF); |
| 9605 | + PIS(FIFO_RX_OVF); |
| 9606 | + PIS(BTA); |
| 9607 | + PIS(ECC_NO_CORR); |
| 9608 | + PIS(FIFO_TX_UDF); |
| 9609 | + PIS(PP_BUSY_CHANGE); |
| 9610 | +#undef PIS |
| 9611 | + printk("\n"); |
| 9612 | +} |
| 9613 | + |
| 9614 | +static void print_irq_status_cio(u32 status) |
| 9615 | +{ |
| 9616 | + printk(KERN_DEBUG "DSI CIO IRQ 0x%x: ", status); |
| 9617 | + |
| 9618 | +#define PIS(x) \ |
| 9619 | + if (status & DSI_CIO_IRQ_##x) \ |
| 9620 | + printk(#x " "); |
| 9621 | + PIS(ERRSYNCESC1); |
| 9622 | + PIS(ERRSYNCESC2); |
| 9623 | + PIS(ERRSYNCESC3); |
| 9624 | + PIS(ERRESC1); |
| 9625 | + PIS(ERRESC2); |
| 9626 | + PIS(ERRESC3); |
| 9627 | + PIS(ERRCONTROL1); |
| 9628 | + PIS(ERRCONTROL2); |
| 9629 | + PIS(ERRCONTROL3); |
| 9630 | + PIS(STATEULPS1); |
| 9631 | + PIS(STATEULPS2); |
| 9632 | + PIS(STATEULPS3); |
| 9633 | + PIS(ERRCONTENTIONLP0_1); |
| 9634 | + PIS(ERRCONTENTIONLP1_1); |
| 9635 | + PIS(ERRCONTENTIONLP0_2); |
| 9636 | + PIS(ERRCONTENTIONLP1_2); |
| 9637 | + PIS(ERRCONTENTIONLP0_3); |
| 9638 | + PIS(ERRCONTENTIONLP1_3); |
| 9639 | + PIS(ULPSACTIVENOT_ALL0); |
| 9640 | + PIS(ULPSACTIVENOT_ALL1); |
| 9641 | +#undef PIS |
| 9642 | + |
| 9643 | + printk("\n"); |
| 9644 | +} |
| 9645 | + |
| 9646 | +static int debug_irq; |
| 9647 | + |
| 9648 | +/* called from dss */ |
| 9649 | +void dsi_irq_handler(void) |
| 9650 | +{ |
| 9651 | + u32 irqstatus, vcstatus, ciostatus; |
| 9652 | + int i; |
| 9653 | + |
| 9654 | + irqstatus = dsi_read_reg(DSI_IRQSTATUS); |
| 9655 | + |
| 9656 | + if (irqstatus & DSI_IRQ_ERROR_MASK) { |
| 9657 | + DSSERR("DSI error, irqstatus %x\n", irqstatus); |
| 9658 | + print_irq_status(irqstatus); |
| 9659 | + spin_lock(&dsi.errors_lock); |
| 9660 | + dsi.errors |= irqstatus & DSI_IRQ_ERROR_MASK; |
| 9661 | + spin_unlock(&dsi.errors_lock); |
| 9662 | + } else if (debug_irq) { |
| 9663 | + print_irq_status(irqstatus); |
| 9664 | + } |
| 9665 | + |
| 9666 | + for (i = 0; i < 4; ++i) { |
| 9667 | + if ((irqstatus & (1<<i)) == 0) |
| 9668 | + continue; |
| 9669 | + |
| 9670 | + vcstatus = dsi_read_reg(DSI_VC_IRQSTATUS(i)); |
| 9671 | + |
| 9672 | + if (vcstatus & DSI_VC_IRQ_BTA) |
| 9673 | + complete(&dsi.bta_completion); |
| 9674 | + |
| 9675 | + if (vcstatus & DSI_VC_IRQ_ERROR_MASK) { |
| 9676 | + DSSERR("DSI VC(%d) error, vc irqstatus %x\n", |
| 9677 | + i, vcstatus); |
| 9678 | + print_irq_status_vc(i, vcstatus); |
| 9679 | + } else if (debug_irq) { |
| 9680 | + print_irq_status_vc(i, vcstatus); |
| 9681 | + } |
| 9682 | + |
| 9683 | + dsi_write_reg(DSI_VC_IRQSTATUS(i), vcstatus); |
| 9684 | + } |
| 9685 | + |
| 9686 | + if (irqstatus & DSI_IRQ_COMPLEXIO_ERR) { |
| 9687 | + ciostatus = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); |
| 9688 | + |
| 9689 | + dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, ciostatus); |
| 9690 | + |
| 9691 | + DSSERR("DSI CIO error, cio irqstatus %x\n", ciostatus); |
| 9692 | + print_irq_status_cio(ciostatus); |
| 9693 | + } |
| 9694 | + |
| 9695 | + dsi_write_reg(DSI_IRQSTATUS, irqstatus & ~DSI_IRQ_CHANNEL_MASK); |
| 9696 | +} |
| 9697 | + |
| 9698 | + |
| 9699 | +static void _dsi_initialize_irq(void) |
| 9700 | +{ |
| 9701 | + u32 l; |
| 9702 | + int i; |
| 9703 | + |
| 9704 | + /* disable all interrupts */ |
| 9705 | + dsi_write_reg(DSI_IRQENABLE, 0); |
| 9706 | + for (i = 0; i < 4; ++i) |
| 9707 | + dsi_write_reg(DSI_VC_IRQENABLE(i), 0); |
| 9708 | + dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, 0); |
| 9709 | + |
| 9710 | + /* clear interrupt status */ |
| 9711 | + l = dsi_read_reg(DSI_IRQSTATUS); |
| 9712 | + dsi_write_reg(DSI_IRQSTATUS, l & ~DSI_IRQ_CHANNEL_MASK); |
| 9713 | + |
| 9714 | + for (i = 0; i < 4; ++i) { |
| 9715 | + l = dsi_read_reg(DSI_VC_IRQSTATUS(i)); |
| 9716 | + dsi_write_reg(DSI_VC_IRQSTATUS(i), l); |
| 9717 | + } |
| 9718 | + |
| 9719 | + l = dsi_read_reg(DSI_COMPLEXIO_IRQ_STATUS); |
| 9720 | + dsi_write_reg(DSI_COMPLEXIO_IRQ_STATUS, l); |
| 9721 | + |
| 9722 | + /* enable error irqs */ |
| 9723 | + l = DSI_IRQ_ERROR_MASK; |
| 9724 | + dsi_write_reg(DSI_IRQENABLE, l); |
| 9725 | + |
| 9726 | + l = DSI_VC_IRQ_ERROR_MASK; |
| 9727 | + for (i = 0; i < 4; ++i) |
| 9728 | + dsi_write_reg(DSI_VC_IRQENABLE(i), l); |
| 9729 | + |
| 9730 | + /* XXX zonda responds incorrectly, causing control error: |
| 9731 | + Exit from LP-ESC mode to LP11 uses wrong transition states on the |
| 9732 | + data lines LP0 and LN0. */ |
| 9733 | + dsi_write_reg(DSI_COMPLEXIO_IRQ_ENABLE, |
| 9734 | + -1 & (~DSI_CIO_IRQ_ERRCONTROL2)); |
| 9735 | +} |
| 9736 | + |
| 9737 | +static u32 dsi_get_errors(void) |
| 9738 | +{ |
| 9739 | + unsigned long flags; |
| 9740 | + u32 e; |
| 9741 | + spin_lock_irqsave(&dsi.errors_lock, flags); |
| 9742 | + e = dsi.errors; |
| 9743 | + dsi.errors = 0; |
| 9744 | + spin_unlock_irqrestore(&dsi.errors_lock, flags); |
| 9745 | + return e; |
| 9746 | +} |
| 9747 | + |
| 9748 | +static void dsi_vc_enable_bta_irq(int channel) |
| 9749 | +{ |
| 9750 | + u32 l; |
| 9751 | + |
| 9752 | + dsi_write_reg(DSI_VC_IRQSTATUS(channel), DSI_VC_IRQ_BTA); |
| 9753 | + |
| 9754 | + l = dsi_read_reg(DSI_VC_IRQENABLE(channel)); |
| 9755 | + l |= DSI_VC_IRQ_BTA; |
| 9756 | + dsi_write_reg(DSI_VC_IRQENABLE(channel), l); |
| 9757 | +} |
| 9758 | + |
| 9759 | +static void dsi_vc_disable_bta_irq(int channel) |
| 9760 | +{ |
| 9761 | + u32 l; |
| 9762 | + |
| 9763 | + l = dsi_read_reg(DSI_VC_IRQENABLE(channel)); |
| 9764 | + l &= ~DSI_VC_IRQ_BTA; |
| 9765 | + dsi_write_reg(DSI_VC_IRQENABLE(channel), l); |
| 9766 | +} |
| 9767 | + |
| 9768 | +/* DSI func clock. this could also be DSI2_PLL_FCLK */ |
| 9769 | +static inline void enable_clocks(bool enable) |
| 9770 | +{ |
| 9771 | + if (enable) |
| 9772 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 9773 | + else |
| 9774 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 9775 | +} |
| 9776 | + |
| 9777 | +/* source clock for DSI PLL. this could also be PCLKFREE */ |
| 9778 | +static inline void dsi_enable_pll_clock(bool enable) |
| 9779 | +{ |
| 9780 | + if (enable) |
| 9781 | + dss_clk_enable(DSS_CLK_FCK2); |
| 9782 | + else |
| 9783 | + dss_clk_disable(DSS_CLK_FCK2); |
| 9784 | + |
| 9785 | + if (enable && dsi.pll_locked) { |
| 9786 | + if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) |
| 9787 | + DSSERR("cannot lock PLL when enabling clocks\n"); |
| 9788 | + } |
| 9789 | +} |
| 9790 | + |
| 9791 | +#ifdef DEBUG |
| 9792 | +static void _dsi_print_reset_status(void) |
| 9793 | +{ |
| 9794 | + u32 l; |
| 9795 | + |
| 9796 | + if (!dss_debug) |
| 9797 | + return; |
| 9798 | + |
| 9799 | + /* A dummy read using the SCP interface to any DSIPHY register is |
| 9800 | + * required after DSIPHY reset to complete the reset of the DSI complex |
| 9801 | + * I/O. */ |
| 9802 | + l = dsi_read_reg(DSI_DSIPHY_CFG5); |
| 9803 | + |
| 9804 | + printk(KERN_DEBUG "DSI resets: "); |
| 9805 | + |
| 9806 | + l = dsi_read_reg(DSI_PLL_STATUS); |
| 9807 | + printk("PLL (%d) ", FLD_GET(l, 0, 0)); |
| 9808 | + |
| 9809 | + l = dsi_read_reg(DSI_COMPLEXIO_CFG1); |
| 9810 | + printk("CIO (%d) ", FLD_GET(l, 29, 29)); |
| 9811 | + |
| 9812 | + l = dsi_read_reg(DSI_DSIPHY_CFG5); |
| 9813 | + printk("PHY (%x, %d, %d, %d)\n", |
| 9814 | + FLD_GET(l, 28, 26), |
| 9815 | + FLD_GET(l, 29, 29), |
| 9816 | + FLD_GET(l, 30, 30), |
| 9817 | + FLD_GET(l, 31, 31)); |
| 9818 | +} |
| 9819 | +#else |
| 9820 | +#define _dsi_print_reset_status() |
| 9821 | +#endif |
| 9822 | + |
| 9823 | +static inline int dsi_if_enable(bool enable) |
| 9824 | +{ |
| 9825 | + DSSDBG("dsi_if_enable(%d)\n", enable); |
| 9826 | + |
| 9827 | + enable = enable ? 1 : 0; |
| 9828 | + REG_FLD_MOD(DSI_CTRL, enable, 0, 0); /* IF_EN */ |
| 9829 | + |
| 9830 | + if (wait_for_bit_change(DSI_CTRL, 0, enable) != enable) { |
| 9831 | + DSSERR("Failed to set dsi_if_enable to %d\n", enable); |
| 9832 | + return -EIO; |
| 9833 | + } |
| 9834 | + |
| 9835 | + return 0; |
| 9836 | +} |
| 9837 | + |
| 9838 | +static unsigned long dsi_fclk_rate(void) |
| 9839 | +{ |
| 9840 | + unsigned long r; |
| 9841 | + |
| 9842 | + if (dss_get_dsi_clk_source() == 0) { |
| 9843 | + /* DSI FCLK source is DSS1_ALWON_FCK, which is dss1_fck */ |
| 9844 | + r = dss_clk_get_rate(DSS_CLK_FCK1); |
| 9845 | + } else { |
| 9846 | + /* DSI FCLK source is DSI2_PLL_FCLK */ |
| 9847 | + r = dsi.dsi2_pll_fclk; |
| 9848 | + } |
| 9849 | + |
| 9850 | + return r; |
| 9851 | +} |
| 9852 | + |
| 9853 | +static int dsi_set_lp_clk_divisor(struct omap_dss_device *dssdev) |
| 9854 | +{ |
| 9855 | + unsigned n; |
| 9856 | + unsigned long dsi_fclk; |
| 9857 | + unsigned long lp_clk, lp_clk_req; |
| 9858 | + |
| 9859 | + dsi_fclk = dsi_fclk_rate(); |
| 9860 | + |
| 9861 | + lp_clk_req = dssdev->phy.dsi.lp_clk_hz; |
| 9862 | + |
| 9863 | + for (n = 1; n < (1 << 13) - 1; ++n) { |
| 9864 | + lp_clk = dsi_fclk / 2 / n; |
| 9865 | + if (lp_clk <= lp_clk_req) |
| 9866 | + break; |
| 9867 | + } |
| 9868 | + |
| 9869 | + if (n == (1 << 13) - 1) { |
| 9870 | + DSSERR("Failed to find LP_CLK_DIVISOR\n"); |
| 9871 | + return -EINVAL; |
| 9872 | + } |
| 9873 | + |
| 9874 | + DSSDBG("LP_CLK_DIV %u, LP_CLK %lu (req %lu)\n", n, lp_clk, lp_clk_req); |
| 9875 | + |
| 9876 | + REG_FLD_MOD(DSI_CLK_CTRL, n, 12, 0); /* LP_CLK_DIVISOR */ |
| 9877 | + if (dsi_fclk > 30*1000*1000) |
| 9878 | + REG_FLD_MOD(DSI_CLK_CTRL, 1, 21, 21); /* LP_RX_SYNCHRO_ENABLE */ |
| 9879 | + |
| 9880 | + return 0; |
| 9881 | +} |
| 9882 | + |
| 9883 | + |
| 9884 | +enum dsi_pll_power_state { |
| 9885 | + DSI_PLL_POWER_OFF = 0x0, |
| 9886 | + DSI_PLL_POWER_ON_HSCLK = 0x1, |
| 9887 | + DSI_PLL_POWER_ON_ALL = 0x2, |
| 9888 | + DSI_PLL_POWER_ON_DIV = 0x3, |
| 9889 | +}; |
| 9890 | + |
| 9891 | +static int dsi_pll_power(enum dsi_pll_power_state state) |
| 9892 | +{ |
| 9893 | + int t = 0; |
| 9894 | + |
| 9895 | + REG_FLD_MOD(DSI_CLK_CTRL, state, 31, 30); /* PLL_PWR_CMD */ |
| 9896 | + |
| 9897 | + /* PLL_PWR_STATUS */ |
| 9898 | + while (FLD_GET(dsi_read_reg(DSI_CLK_CTRL), 29, 28) != state) { |
| 9899 | + udelay(1); |
| 9900 | + if (t++ > 1000) { |
| 9901 | + DSSERR("Failed to set DSI PLL power mode to %d\n", |
| 9902 | + state); |
| 9903 | + return -ENODEV; |
| 9904 | + } |
| 9905 | + } |
| 9906 | + |
| 9907 | + return 0; |
| 9908 | +} |
| 9909 | + |
| 9910 | +int dsi_pll_calc_pck(bool is_tft, unsigned long req_pck, |
| 9911 | + struct dsi_clock_info *cinfo) |
| 9912 | +{ |
| 9913 | + struct dsi_clock_info cur, best; |
| 9914 | + int min_fck_per_pck; |
| 9915 | + int match = 0; |
| 9916 | + unsigned long dss_clk_fck2; |
| 9917 | + |
| 9918 | + dss_clk_fck2 = dss_clk_get_rate(DSS_CLK_FCK2); |
| 9919 | + |
| 9920 | + if (req_pck == dsi.cache_req_pck && |
| 9921 | + dsi.cache_cinfo.clkin == dss_clk_fck2) { |
| 9922 | + DSSDBG("DSI clock info found from cache\n"); |
| 9923 | + *cinfo = dsi.cache_cinfo; |
| 9924 | + return 0; |
| 9925 | + } |
| 9926 | + |
| 9927 | + min_fck_per_pck = CONFIG_OMAP2_DSS_MIN_FCK_PER_PCK; |
| 9928 | + |
| 9929 | + if (min_fck_per_pck && |
| 9930 | + req_pck * min_fck_per_pck > DISPC_MAX_FCK) { |
| 9931 | + DSSERR("Requested pixel clock not possible with the current " |
| 9932 | + "OMAP2_DSS_MIN_FCK_PER_PCK setting. Turning " |
| 9933 | + "the constraint off.\n"); |
| 9934 | + min_fck_per_pck = 0; |
| 9935 | + } |
| 9936 | + |
| 9937 | + DSSDBG("dsi_pll_calc\n"); |
| 9938 | + |
| 9939 | +retry: |
| 9940 | + memset(&best, 0, sizeof(best)); |
| 9941 | + |
| 9942 | + memset(&cur, 0, sizeof(cur)); |
| 9943 | + cur.clkin = dss_clk_fck2; |
| 9944 | + cur.use_dss2_fck = 1; |
| 9945 | + cur.highfreq = 0; |
| 9946 | + |
| 9947 | + /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */ |
| 9948 | + /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */ |
| 9949 | + /* To reduce PLL lock time, keep Fint high (around 2 MHz) */ |
| 9950 | + for (cur.regn = 1; cur.regn < REGN_MAX; ++cur.regn) { |
| 9951 | + if (cur.highfreq == 0) |
| 9952 | + cur.fint = cur.clkin / cur.regn; |
| 9953 | + else |
| 9954 | + cur.fint = cur.clkin / (2 * cur.regn); |
| 9955 | + |
| 9956 | + if (cur.fint > FINT_MAX || cur.fint < FINT_MIN) |
| 9957 | + continue; |
| 9958 | + |
| 9959 | + /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */ |
| 9960 | + for (cur.regm = 1; cur.regm < REGM_MAX; ++cur.regm) { |
| 9961 | + unsigned long a, b; |
| 9962 | + |
| 9963 | + a = 2 * cur.regm * (cur.clkin/1000); |
| 9964 | + b = cur.regn * (cur.highfreq + 1); |
| 9965 | + cur.dsiphy = a / b * 1000; |
| 9966 | + |
| 9967 | + if (cur.dsiphy > 1800 * 1000 * 1000) |
| 9968 | + break; |
| 9969 | + |
| 9970 | + /* DSI1_PLL_FCLK(MHz) = DSIPHY(MHz) / regm3 < 173MHz */ |
| 9971 | + for (cur.regm3 = 1; cur.regm3 < REGM3_MAX; |
| 9972 | + ++cur.regm3) { |
| 9973 | + cur.dsi1_pll_fclk = cur.dsiphy / cur.regm3; |
| 9974 | + |
| 9975 | + /* this will narrow down the search a bit, |
| 9976 | + * but still give pixclocks below what was |
| 9977 | + * requested */ |
| 9978 | + if (cur.dsi1_pll_fclk < req_pck) |
| 9979 | + break; |
| 9980 | + |
| 9981 | + if (cur.dsi1_pll_fclk > DISPC_MAX_FCK) |
| 9982 | + continue; |
| 9983 | + |
| 9984 | + if (min_fck_per_pck && |
| 9985 | + cur.dsi1_pll_fclk < |
| 9986 | + req_pck * min_fck_per_pck) |
| 9987 | + continue; |
| 9988 | + |
| 9989 | + match = 1; |
| 9990 | + |
| 9991 | + find_lck_pck_divs(is_tft, req_pck, |
| 9992 | + cur.dsi1_pll_fclk, |
| 9993 | + &cur.lck_div, |
| 9994 | + &cur.pck_div); |
| 9995 | + |
| 9996 | + cur.lck = cur.dsi1_pll_fclk / cur.lck_div; |
| 9997 | + cur.pck = cur.lck / cur.pck_div; |
| 9998 | + |
| 9999 | + if (abs(cur.pck - req_pck) < |
| 10000 | + abs(best.pck - req_pck)) { |
| 10001 | + best = cur; |
| 10002 | + |
| 10003 | + if (cur.pck == req_pck) |
| 10004 | + goto found; |
| 10005 | + } |
| 10006 | + } |
| 10007 | + } |
| 10008 | + } |
| 10009 | +found: |
| 10010 | + if (!match) { |
| 10011 | + if (min_fck_per_pck) { |
| 10012 | + DSSERR("Could not find suitable clock settings.\n" |
| 10013 | + "Turning FCK/PCK constraint off and" |
| 10014 | + "trying again.\n"); |
| 10015 | + min_fck_per_pck = 0; |
| 10016 | + goto retry; |
| 10017 | + } |
| 10018 | + |
| 10019 | + DSSERR("Could not find suitable clock settings.\n"); |
| 10020 | + |
| 10021 | + return -EINVAL; |
| 10022 | + } |
| 10023 | + |
| 10024 | + /* DSI2_PLL_FCLK (regm4) is not used. Set it to something sane. */ |
| 10025 | + best.regm4 = best.dsiphy / 48000000; |
| 10026 | + if (best.regm4 > REGM4_MAX) |
| 10027 | + best.regm4 = REGM4_MAX; |
| 10028 | + else if (best.regm4 == 0) |
| 10029 | + best.regm4 = 1; |
| 10030 | + best.dsi2_pll_fclk = best.dsiphy / best.regm4; |
| 10031 | + |
| 10032 | + if (cinfo) |
| 10033 | + *cinfo = best; |
| 10034 | + |
| 10035 | + dsi.cache_req_pck = req_pck; |
| 10036 | + dsi.cache_clk_freq = 0; |
| 10037 | + dsi.cache_cinfo = best; |
| 10038 | + |
| 10039 | + return 0; |
| 10040 | +} |
| 10041 | + |
| 10042 | +static int dsi_pll_calc_ddrfreq(unsigned long clk_freq, |
| 10043 | + struct dsi_clock_info *cinfo) |
| 10044 | +{ |
| 10045 | + struct dsi_clock_info cur, best; |
| 10046 | + const bool use_dss2_fck = 1; |
| 10047 | + unsigned long datafreq; |
| 10048 | + unsigned long dss_clk_fck2; |
| 10049 | + |
| 10050 | + DSSDBG("dsi_pll_calc_ddrfreq\n"); |
| 10051 | + |
| 10052 | + dss_clk_fck2 = dss_clk_get_rate(DSS_CLK_FCK2); |
| 10053 | + |
| 10054 | + if (clk_freq == dsi.cache_clk_freq && |
| 10055 | + dsi.cache_cinfo.clkin == dss_clk_fck2) { |
| 10056 | + DSSDBG("DSI clock info found from cache\n"); |
| 10057 | + *cinfo = dsi.cache_cinfo; |
| 10058 | + return 0; |
| 10059 | + } |
| 10060 | + |
| 10061 | + datafreq = clk_freq * 4; |
| 10062 | + |
| 10063 | + memset(&best, 0, sizeof(best)); |
| 10064 | + |
| 10065 | + memset(&cur, 0, sizeof(cur)); |
| 10066 | + cur.use_dss2_fck = use_dss2_fck; |
| 10067 | + if (use_dss2_fck) { |
| 10068 | + cur.clkin = dss_clk_fck2; |
| 10069 | + cur.highfreq = 0; |
| 10070 | + } else { |
| 10071 | + cur.clkin = dispc_pclk_rate(); |
| 10072 | + if (cur.clkin < 32000000) |
| 10073 | + cur.highfreq = 0; |
| 10074 | + else |
| 10075 | + cur.highfreq = 1; |
| 10076 | + } |
| 10077 | + |
| 10078 | + /* no highfreq: 0.75MHz < Fint = clkin / regn < 2.1MHz */ |
| 10079 | + /* highfreq: 0.75MHz < Fint = clkin / (2*regn) < 2.1MHz */ |
| 10080 | + /* To reduce PLL lock time, keep Fint high (around 2 MHz) */ |
| 10081 | + for (cur.regn = 1; cur.regn < REGN_MAX; ++cur.regn) { |
| 10082 | + if (cur.highfreq == 0) |
| 10083 | + cur.fint = cur.clkin / cur.regn; |
| 10084 | + else |
| 10085 | + cur.fint = cur.clkin / (2 * cur.regn); |
| 10086 | + |
| 10087 | + if (cur.fint > FINT_MAX || cur.fint < FINT_MIN) |
| 10088 | + continue; |
| 10089 | + |
| 10090 | + /* DSIPHY(MHz) = (2 * regm / regn) * (clkin / (highfreq + 1)) */ |
| 10091 | + for (cur.regm = 1; cur.regm < REGM_MAX; ++cur.regm) { |
| 10092 | + unsigned long a, b; |
| 10093 | + |
| 10094 | + a = 2 * cur.regm * (cur.clkin/1000); |
| 10095 | + b = cur.regn * (cur.highfreq + 1); |
| 10096 | + cur.dsiphy = a / b * 1000; |
| 10097 | + |
| 10098 | + if (cur.dsiphy > 1800 * 1000 * 1000) |
| 10099 | + break; |
| 10100 | + |
| 10101 | + if (abs(cur.dsiphy - datafreq) < |
| 10102 | + abs(best.dsiphy - datafreq)) { |
| 10103 | + best = cur; |
| 10104 | + /* DSSDBG("best %ld\n", best.dsiphy); */ |
| 10105 | + } |
| 10106 | + |
| 10107 | + if (cur.dsiphy == datafreq) |
| 10108 | + goto found; |
| 10109 | + } |
| 10110 | + } |
| 10111 | +found: |
| 10112 | + /* DSI1_PLL_FCLK (regm3) is not used. Set it to something sane. */ |
| 10113 | + best.regm3 = best.dsiphy / 48000000; |
| 10114 | + if (best.regm3 > REGM3_MAX) |
| 10115 | + best.regm3 = REGM3_MAX; |
| 10116 | + else if (best.regm3 == 0) |
| 10117 | + best.regm3 = 1; |
| 10118 | + best.dsi1_pll_fclk = best.dsiphy / best.regm3; |
| 10119 | + |
| 10120 | + /* DSI2_PLL_FCLK (regm4) is not used. Set it to something sane. */ |
| 10121 | + best.regm4 = best.dsiphy / 48000000; |
| 10122 | + if (best.regm4 > REGM4_MAX) |
| 10123 | + best.regm4 = REGM4_MAX; |
| 10124 | + else if (best.regm4 == 0) |
| 10125 | + best.regm4 = 1; |
| 10126 | + best.dsi2_pll_fclk = best.dsiphy / best.regm4; |
| 10127 | + |
| 10128 | + if (cinfo) |
| 10129 | + *cinfo = best; |
| 10130 | + |
| 10131 | + dsi.cache_clk_freq = clk_freq; |
| 10132 | + dsi.cache_req_pck = 0; |
| 10133 | + dsi.cache_cinfo = best; |
| 10134 | + |
| 10135 | + return 0; |
| 10136 | +} |
| 10137 | + |
| 10138 | +int dsi_pll_program(struct dsi_clock_info *cinfo) |
| 10139 | +{ |
| 10140 | + int r = 0; |
| 10141 | + u32 l; |
| 10142 | + |
| 10143 | + DSSDBG("dsi_pll_program\n"); |
| 10144 | + |
| 10145 | + dsi.dsiphy = cinfo->dsiphy; |
| 10146 | + dsi.ddr_clk = dsi.dsiphy / 4; |
| 10147 | + dsi.dsi1_pll_fclk = cinfo->dsi1_pll_fclk; |
| 10148 | + dsi.dsi2_pll_fclk = cinfo->dsi2_pll_fclk; |
| 10149 | + |
| 10150 | + DSSDBG("DSI Fint %ld\n", cinfo->fint); |
| 10151 | + |
| 10152 | + DSSDBG("clkin (%s) rate %ld, highfreq %d\n", |
| 10153 | + cinfo->use_dss2_fck ? "dss2_fck" : "pclkfree", |
| 10154 | + cinfo->clkin, |
| 10155 | + cinfo->highfreq); |
| 10156 | + |
| 10157 | + /* DSIPHY == CLKIN4DDR */ |
| 10158 | + DSSDBG("DSIPHY = 2 * %d / %d * %lu / %d = %lu\n", |
| 10159 | + cinfo->regm, |
| 10160 | + cinfo->regn, |
| 10161 | + cinfo->clkin, |
| 10162 | + cinfo->highfreq + 1, |
| 10163 | + cinfo->dsiphy); |
| 10164 | + |
| 10165 | + DSSDBG("Data rate on 1 DSI lane %ld Mbps\n", |
| 10166 | + dsi.dsiphy / 1000 / 1000 / 2); |
| 10167 | + |
| 10168 | + DSSDBG("Clock lane freq %ld Hz\n", dsi.ddr_clk); |
| 10169 | + |
| 10170 | + DSSDBG("regm3 = %d, dsi1_pll_fclk = %lu\n", |
| 10171 | + cinfo->regm3, cinfo->dsi1_pll_fclk); |
| 10172 | + DSSDBG("regm4 = %d, dsi2_pll_fclk = %lu\n", |
| 10173 | + cinfo->regm4, cinfo->dsi2_pll_fclk); |
| 10174 | + |
| 10175 | + REG_FLD_MOD(DSI_PLL_CONTROL, 0, 0, 0); /* DSI_PLL_AUTOMODE = manual */ |
| 10176 | + |
| 10177 | + l = dsi_read_reg(DSI_PLL_CONFIGURATION1); |
| 10178 | + l = FLD_MOD(l, 1, 0, 0); /* DSI_PLL_STOPMODE */ |
| 10179 | + l = FLD_MOD(l, cinfo->regn - 1, 7, 1); /* DSI_PLL_REGN */ |
| 10180 | + l = FLD_MOD(l, cinfo->regm, 18, 8); /* DSI_PLL_REGM */ |
| 10181 | + l = FLD_MOD(l, cinfo->regm3 - 1, 22, 19); /* DSI_CLOCK_DIV */ |
| 10182 | + l = FLD_MOD(l, cinfo->regm4 - 1, 26, 23); /* DSIPROTO_CLOCK_DIV */ |
| 10183 | + dsi_write_reg(DSI_PLL_CONFIGURATION1, l); |
| 10184 | + |
| 10185 | + l = dsi_read_reg(DSI_PLL_CONFIGURATION2); |
| 10186 | + l = FLD_MOD(l, 7, 4, 1); /* DSI_PLL_FREQSEL */ |
| 10187 | + /* DSI_PLL_CLKSEL */ |
| 10188 | + l = FLD_MOD(l, cinfo->use_dss2_fck ? 0 : 1, 11, 11); |
| 10189 | + l = FLD_MOD(l, cinfo->highfreq, 12, 12); /* DSI_PLL_HIGHFREQ */ |
| 10190 | + l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ |
| 10191 | + l = FLD_MOD(l, 0, 14, 14); /* DSIPHY_CLKINEN */ |
| 10192 | + l = FLD_MOD(l, 1, 20, 20); /* DSI_HSDIVBYPASS */ |
| 10193 | + dsi_write_reg(DSI_PLL_CONFIGURATION2, l); |
| 10194 | + |
| 10195 | + REG_FLD_MOD(DSI_PLL_GO, 1, 0, 0); /* DSI_PLL_GO */ |
| 10196 | + |
| 10197 | + if (wait_for_bit_change(DSI_PLL_GO, 0, 0) != 0) { |
| 10198 | + DSSERR("dsi pll go bit not going down.\n"); |
| 10199 | + r = -EIO; |
| 10200 | + goto err; |
| 10201 | + } |
| 10202 | + |
| 10203 | + if (wait_for_bit_change(DSI_PLL_STATUS, 1, 1) != 1) { |
| 10204 | + DSSERR("cannot lock PLL\n"); |
| 10205 | + r = -EIO; |
| 10206 | + goto err; |
| 10207 | + } |
| 10208 | + |
| 10209 | + dsi.pll_locked = 1; |
| 10210 | + |
| 10211 | + l = dsi_read_reg(DSI_PLL_CONFIGURATION2); |
| 10212 | + l = FLD_MOD(l, 0, 0, 0); /* DSI_PLL_IDLE */ |
| 10213 | + l = FLD_MOD(l, 0, 5, 5); /* DSI_PLL_PLLLPMODE */ |
| 10214 | + l = FLD_MOD(l, 0, 6, 6); /* DSI_PLL_LOWCURRSTBY */ |
| 10215 | + l = FLD_MOD(l, 0, 7, 7); /* DSI_PLL_TIGHTPHASELOCK */ |
| 10216 | + l = FLD_MOD(l, 0, 8, 8); /* DSI_PLL_DRIFTGUARDEN */ |
| 10217 | + l = FLD_MOD(l, 0, 10, 9); /* DSI_PLL_LOCKSEL */ |
| 10218 | + l = FLD_MOD(l, 1, 13, 13); /* DSI_PLL_REFEN */ |
| 10219 | + l = FLD_MOD(l, 1, 14, 14); /* DSIPHY_CLKINEN */ |
| 10220 | + l = FLD_MOD(l, 0, 15, 15); /* DSI_BYPASSEN */ |
| 10221 | + l = FLD_MOD(l, 1, 16, 16); /* DSS_CLOCK_EN */ |
| 10222 | + l = FLD_MOD(l, 0, 17, 17); /* DSS_CLOCK_PWDN */ |
| 10223 | + l = FLD_MOD(l, 1, 18, 18); /* DSI_PROTO_CLOCK_EN */ |
| 10224 | + l = FLD_MOD(l, 0, 19, 19); /* DSI_PROTO_CLOCK_PWDN */ |
| 10225 | + l = FLD_MOD(l, 0, 20, 20); /* DSI_HSDIVBYPASS */ |
| 10226 | + dsi_write_reg(DSI_PLL_CONFIGURATION2, l); |
| 10227 | + |
| 10228 | + DSSDBG("PLL config done\n"); |
| 10229 | +err: |
| 10230 | + return r; |
| 10231 | +} |
| 10232 | + |
| 10233 | +int dsi_pll_init(bool enable_hsclk, bool enable_hsdiv) |
| 10234 | +{ |
| 10235 | + int r = 0; |
| 10236 | + enum dsi_pll_power_state pwstate; |
| 10237 | + struct dispc_clock_info cinfo; |
| 10238 | + |
| 10239 | + DSSDBG("PLL init\n"); |
| 10240 | + |
| 10241 | + enable_clocks(1); |
| 10242 | + dsi_enable_pll_clock(1); |
| 10243 | + |
| 10244 | + /* XXX this should be calculated depending on the screen size, |
| 10245 | + * required framerate and DSI speed. |
| 10246 | + * For now 48MHz is enough for 864x480@60 with 360Mbps/lane |
| 10247 | + * with two lanes */ |
| 10248 | + r = dispc_calc_clock_div(1, 48 * 1000 * 1000, &cinfo); |
| 10249 | + if (r) |
| 10250 | + goto err0; |
| 10251 | + |
| 10252 | + r = dispc_set_clock_div(&cinfo); |
| 10253 | + if (r) { |
| 10254 | + DSSERR("Failed to set basic clocks\n"); |
| 10255 | + goto err0; |
| 10256 | + } |
| 10257 | + |
| 10258 | + r = regulator_enable(dsi.vdds_dsi_reg); |
| 10259 | + if (r) |
| 10260 | + goto err0; |
| 10261 | + |
| 10262 | + /* XXX PLL does not come out of reset without this... */ |
| 10263 | + dispc_pck_free_enable(1); |
| 10264 | + |
| 10265 | + if (wait_for_bit_change(DSI_PLL_STATUS, 0, 1) != 1) { |
| 10266 | + DSSERR("PLL not coming out of reset.\n"); |
| 10267 | + r = -ENODEV; |
| 10268 | + goto err1; |
| 10269 | + } |
| 10270 | + |
| 10271 | + /* XXX ... but if left on, we get problems when planes do not |
| 10272 | + * fill the whole display. No idea about this */ |
| 10273 | + dispc_pck_free_enable(0); |
| 10274 | + |
| 10275 | + if (enable_hsclk && enable_hsdiv) |
| 10276 | + pwstate = DSI_PLL_POWER_ON_ALL; |
| 10277 | + else if (enable_hsclk) |
| 10278 | + pwstate = DSI_PLL_POWER_ON_HSCLK; |
| 10279 | + else if (enable_hsdiv) |
| 10280 | + pwstate = DSI_PLL_POWER_ON_DIV; |
| 10281 | + else |
| 10282 | + pwstate = DSI_PLL_POWER_OFF; |
| 10283 | + |
| 10284 | + r = dsi_pll_power(pwstate); |
| 10285 | + |
| 10286 | + if (r) |
| 10287 | + goto err1; |
| 10288 | + |
| 10289 | + DSSDBG("PLL init done\n"); |
| 10290 | + |
| 10291 | + return 0; |
| 10292 | +err1: |
| 10293 | + regulator_disable(dsi.vdds_dsi_reg); |
| 10294 | +err0: |
| 10295 | + enable_clocks(0); |
| 10296 | + dsi_enable_pll_clock(0); |
| 10297 | + return r; |
| 10298 | +} |
| 10299 | + |
| 10300 | +void dsi_pll_uninit(void) |
| 10301 | +{ |
| 10302 | + enable_clocks(0); |
| 10303 | + dsi_enable_pll_clock(0); |
| 10304 | + |
| 10305 | + dsi.pll_locked = 0; |
| 10306 | + dsi_pll_power(DSI_PLL_POWER_OFF); |
| 10307 | + regulator_disable(dsi.vdds_dsi_reg); |
| 10308 | + DSSDBG("PLL uninit done\n"); |
| 10309 | +} |
| 10310 | + |
| 10311 | +unsigned long dsi_get_dsi1_pll_rate(void) |
| 10312 | +{ |
| 10313 | + return dsi.dsi1_pll_fclk; |
| 10314 | +} |
| 10315 | + |
| 10316 | +unsigned long dsi_get_dsi2_pll_rate(void) |
| 10317 | +{ |
| 10318 | + return dsi.dsi2_pll_fclk; |
| 10319 | +} |
| 10320 | + |
| 10321 | +void dsi_dump_clocks(struct seq_file *s) |
| 10322 | +{ |
| 10323 | + int clksel; |
| 10324 | + |
| 10325 | + enable_clocks(1); |
| 10326 | + |
| 10327 | + clksel = REG_GET(DSI_PLL_CONFIGURATION2, 11, 11); |
| 10328 | + |
| 10329 | + seq_printf(s, "- dsi -\n"); |
| 10330 | + |
| 10331 | + seq_printf(s, "dsi fclk source = %s\n", |
| 10332 | + dss_get_dsi_clk_source() == 0 ? |
| 10333 | + "dss1_alwon_fclk" : "dsi2_pll_fclk"); |
| 10334 | + |
| 10335 | + seq_printf(s, "dsi pll source = %s\n", |
| 10336 | + clksel == 0 ? |
| 10337 | + "dss2_alwon_fclk" : "pclkfree"); |
| 10338 | + |
| 10339 | + seq_printf(s, "DSIPHY\t\t%lu\nDDR_CLK\t\t%lu\n", |
| 10340 | + dsi.dsiphy, dsi.ddr_clk); |
| 10341 | + |
| 10342 | + seq_printf(s, "dsi1_pll_fck\t%lu (%s)\n" |
| 10343 | + "dsi2_pll_fck\t%lu (%s)\n", |
| 10344 | + dsi.dsi1_pll_fclk, |
| 10345 | + dss_get_dispc_clk_source() == 0 ? "off" : "on", |
| 10346 | + dsi.dsi2_pll_fclk, |
| 10347 | + dss_get_dsi_clk_source() == 0 ? "off" : "on"); |
| 10348 | + |
| 10349 | + enable_clocks(0); |
| 10350 | +} |
| 10351 | + |
| 10352 | +void dsi_dump_regs(struct seq_file *s) |
| 10353 | +{ |
| 10354 | +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dsi_read_reg(r)) |
| 10355 | + |
| 10356 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 10357 | + |
| 10358 | + DUMPREG(DSI_REVISION); |
| 10359 | + DUMPREG(DSI_SYSCONFIG); |
| 10360 | + DUMPREG(DSI_SYSSTATUS); |
| 10361 | + DUMPREG(DSI_IRQSTATUS); |
| 10362 | + DUMPREG(DSI_IRQENABLE); |
| 10363 | + DUMPREG(DSI_CTRL); |
| 10364 | + DUMPREG(DSI_COMPLEXIO_CFG1); |
| 10365 | + DUMPREG(DSI_COMPLEXIO_IRQ_STATUS); |
| 10366 | + DUMPREG(DSI_COMPLEXIO_IRQ_ENABLE); |
| 10367 | + DUMPREG(DSI_CLK_CTRL); |
| 10368 | + DUMPREG(DSI_TIMING1); |
| 10369 | + DUMPREG(DSI_TIMING2); |
| 10370 | + DUMPREG(DSI_VM_TIMING1); |
| 10371 | + DUMPREG(DSI_VM_TIMING2); |
| 10372 | + DUMPREG(DSI_VM_TIMING3); |
| 10373 | + DUMPREG(DSI_CLK_TIMING); |
| 10374 | + DUMPREG(DSI_TX_FIFO_VC_SIZE); |
| 10375 | + DUMPREG(DSI_RX_FIFO_VC_SIZE); |
| 10376 | + DUMPREG(DSI_COMPLEXIO_CFG2); |
| 10377 | + DUMPREG(DSI_RX_FIFO_VC_FULLNESS); |
| 10378 | + DUMPREG(DSI_VM_TIMING4); |
| 10379 | + DUMPREG(DSI_TX_FIFO_VC_EMPTINESS); |
| 10380 | + DUMPREG(DSI_VM_TIMING5); |
| 10381 | + DUMPREG(DSI_VM_TIMING6); |
| 10382 | + DUMPREG(DSI_VM_TIMING7); |
| 10383 | + DUMPREG(DSI_STOPCLK_TIMING); |
| 10384 | + |
| 10385 | + DUMPREG(DSI_VC_CTRL(0)); |
| 10386 | + DUMPREG(DSI_VC_TE(0)); |
| 10387 | + DUMPREG(DSI_VC_LONG_PACKET_HEADER(0)); |
| 10388 | + DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(0)); |
| 10389 | + DUMPREG(DSI_VC_SHORT_PACKET_HEADER(0)); |
| 10390 | + DUMPREG(DSI_VC_IRQSTATUS(0)); |
| 10391 | + DUMPREG(DSI_VC_IRQENABLE(0)); |
| 10392 | + |
| 10393 | + DUMPREG(DSI_VC_CTRL(1)); |
| 10394 | + DUMPREG(DSI_VC_TE(1)); |
| 10395 | + DUMPREG(DSI_VC_LONG_PACKET_HEADER(1)); |
| 10396 | + DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(1)); |
| 10397 | + DUMPREG(DSI_VC_SHORT_PACKET_HEADER(1)); |
| 10398 | + DUMPREG(DSI_VC_IRQSTATUS(1)); |
| 10399 | + DUMPREG(DSI_VC_IRQENABLE(1)); |
| 10400 | + |
| 10401 | + DUMPREG(DSI_VC_CTRL(2)); |
| 10402 | + DUMPREG(DSI_VC_TE(2)); |
| 10403 | + DUMPREG(DSI_VC_LONG_PACKET_HEADER(2)); |
| 10404 | + DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(2)); |
| 10405 | + DUMPREG(DSI_VC_SHORT_PACKET_HEADER(2)); |
| 10406 | + DUMPREG(DSI_VC_IRQSTATUS(2)); |
| 10407 | + DUMPREG(DSI_VC_IRQENABLE(2)); |
| 10408 | + |
| 10409 | + DUMPREG(DSI_VC_CTRL(3)); |
| 10410 | + DUMPREG(DSI_VC_TE(3)); |
| 10411 | + DUMPREG(DSI_VC_LONG_PACKET_HEADER(3)); |
| 10412 | + DUMPREG(DSI_VC_LONG_PACKET_PAYLOAD(3)); |
| 10413 | + DUMPREG(DSI_VC_SHORT_PACKET_HEADER(3)); |
| 10414 | + DUMPREG(DSI_VC_IRQSTATUS(3)); |
| 10415 | + DUMPREG(DSI_VC_IRQENABLE(3)); |
| 10416 | + |
| 10417 | + DUMPREG(DSI_DSIPHY_CFG0); |
| 10418 | + DUMPREG(DSI_DSIPHY_CFG1); |
| 10419 | + DUMPREG(DSI_DSIPHY_CFG2); |
| 10420 | + DUMPREG(DSI_DSIPHY_CFG5); |
| 10421 | + |
| 10422 | + DUMPREG(DSI_PLL_CONTROL); |
| 10423 | + DUMPREG(DSI_PLL_STATUS); |
| 10424 | + DUMPREG(DSI_PLL_GO); |
| 10425 | + DUMPREG(DSI_PLL_CONFIGURATION1); |
| 10426 | + DUMPREG(DSI_PLL_CONFIGURATION2); |
| 10427 | + |
| 10428 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 10429 | +#undef DUMPREG |
| 10430 | +} |
| 10431 | + |
| 10432 | +enum dsi_complexio_power_state { |
| 10433 | + DSI_COMPLEXIO_POWER_OFF = 0x0, |
| 10434 | + DSI_COMPLEXIO_POWER_ON = 0x1, |
| 10435 | + DSI_COMPLEXIO_POWER_ULPS = 0x2, |
| 10436 | +}; |
| 10437 | + |
| 10438 | +static int dsi_complexio_power(enum dsi_complexio_power_state state) |
| 10439 | +{ |
| 10440 | + int t = 0; |
| 10441 | + |
| 10442 | + /* PWR_CMD */ |
| 10443 | + REG_FLD_MOD(DSI_COMPLEXIO_CFG1, state, 28, 27); |
| 10444 | + |
| 10445 | + /* PWR_STATUS */ |
| 10446 | + while (FLD_GET(dsi_read_reg(DSI_COMPLEXIO_CFG1), 26, 25) != state) { |
| 10447 | + udelay(1); |
| 10448 | + if (t++ > 1000) { |
| 10449 | + DSSERR("failed to set complexio power state to " |
| 10450 | + "%d\n", state); |
| 10451 | + return -ENODEV; |
| 10452 | + } |
| 10453 | + } |
| 10454 | + |
| 10455 | + return 0; |
| 10456 | +} |
| 10457 | + |
| 10458 | +static void dsi_complexio_config(struct omap_dss_device *dssdev) |
| 10459 | +{ |
| 10460 | + u32 r; |
| 10461 | + |
| 10462 | + int clk_lane = dssdev->phy.dsi.clk_lane; |
| 10463 | + int data1_lane = dssdev->phy.dsi.data1_lane; |
| 10464 | + int data2_lane = dssdev->phy.dsi.data2_lane; |
| 10465 | + int clk_pol = dssdev->phy.dsi.clk_pol; |
| 10466 | + int data1_pol = dssdev->phy.dsi.data1_pol; |
| 10467 | + int data2_pol = dssdev->phy.dsi.data2_pol; |
| 10468 | + |
| 10469 | + r = dsi_read_reg(DSI_COMPLEXIO_CFG1); |
| 10470 | + r = FLD_MOD(r, clk_lane, 2, 0); |
| 10471 | + r = FLD_MOD(r, clk_pol, 3, 3); |
| 10472 | + r = FLD_MOD(r, data1_lane, 6, 4); |
| 10473 | + r = FLD_MOD(r, data1_pol, 7, 7); |
| 10474 | + r = FLD_MOD(r, data2_lane, 10, 8); |
| 10475 | + r = FLD_MOD(r, data2_pol, 11, 11); |
| 10476 | + dsi_write_reg(DSI_COMPLEXIO_CFG1, r); |
| 10477 | + |
| 10478 | + /* The configuration of the DSI complex I/O (number of data lanes, |
| 10479 | + position, differential order) should not be changed while |
| 10480 | + DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. In order for |
| 10481 | + the hardware to take into account a new configuration of the complex |
| 10482 | + I/O (done in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to |
| 10483 | + follow this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1, |
| 10484 | + then reset the DSS.DSI_CTRL[0] IF_EN to 0, then set |
| 10485 | + DSS.DSI_CLK_CTRL[20] LP_CLK_ENABLE to 1 and finally set again the |
| 10486 | + DSS.DSI_CTRL[0] IF_EN bit to 1. If the sequence is not followed, the |
| 10487 | + DSI complex I/O configuration is unknown. */ |
| 10488 | + |
| 10489 | + /* |
| 10490 | + REG_FLD_MOD(DSI_CTRL, 1, 0, 0); |
| 10491 | + REG_FLD_MOD(DSI_CTRL, 0, 0, 0); |
| 10492 | + REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); |
| 10493 | + REG_FLD_MOD(DSI_CTRL, 1, 0, 0); |
| 10494 | + */ |
| 10495 | +} |
| 10496 | + |
| 10497 | +static inline unsigned ns2ddr(unsigned ns) |
| 10498 | +{ |
| 10499 | + /* convert time in ns to ddr ticks, rounding up */ |
| 10500 | + return (ns * (dsi.ddr_clk/1000/1000) + 999) / 1000; |
| 10501 | +} |
| 10502 | + |
| 10503 | +static inline unsigned ddr2ns(unsigned ddr) |
| 10504 | +{ |
| 10505 | + return ddr * 1000 * 1000 / (dsi.ddr_clk / 1000); |
| 10506 | +} |
| 10507 | + |
| 10508 | +static void dsi_complexio_timings(void) |
| 10509 | +{ |
| 10510 | + u32 r; |
| 10511 | + u32 ths_prepare, ths_prepare_ths_zero, ths_trail, ths_exit; |
| 10512 | + u32 tlpx_half, tclk_trail, tclk_zero; |
| 10513 | + u32 tclk_prepare; |
| 10514 | + |
| 10515 | + /* calculate timings */ |
| 10516 | + |
| 10517 | + /* 1 * DDR_CLK = 2 * UI */ |
| 10518 | + |
| 10519 | + /* min 40ns + 4*UI max 85ns + 6*UI */ |
| 10520 | + ths_prepare = ns2ddr(70) + 2; |
| 10521 | + |
| 10522 | + /* min 145ns + 10*UI */ |
| 10523 | + ths_prepare_ths_zero = ns2ddr(175) + 2; |
| 10524 | + |
| 10525 | + /* min max(8*UI, 60ns+4*UI) */ |
| 10526 | + ths_trail = ns2ddr(60) + 5; |
| 10527 | + |
| 10528 | + /* min 100ns */ |
| 10529 | + ths_exit = ns2ddr(145); |
| 10530 | + |
| 10531 | + /* tlpx min 50n */ |
| 10532 | + tlpx_half = ns2ddr(25); |
| 10533 | + |
| 10534 | + /* min 60ns */ |
| 10535 | + tclk_trail = ns2ddr(60) + 2; |
| 10536 | + |
| 10537 | + /* min 38ns, max 95ns */ |
| 10538 | + tclk_prepare = ns2ddr(65); |
| 10539 | + |
| 10540 | + /* min tclk-prepare + tclk-zero = 300ns */ |
| 10541 | + tclk_zero = ns2ddr(260); |
| 10542 | + |
| 10543 | + DSSDBG("ths_prepare %u (%uns), ths_prepare_ths_zero %u (%uns)\n", |
| 10544 | + ths_prepare, ddr2ns(ths_prepare), |
| 10545 | + ths_prepare_ths_zero, ddr2ns(ths_prepare_ths_zero)); |
| 10546 | + DSSDBG("ths_trail %u (%uns), ths_exit %u (%uns)\n", |
| 10547 | + ths_trail, ddr2ns(ths_trail), |
| 10548 | + ths_exit, ddr2ns(ths_exit)); |
| 10549 | + |
| 10550 | + DSSDBG("tlpx_half %u (%uns), tclk_trail %u (%uns), " |
| 10551 | + "tclk_zero %u (%uns)\n", |
| 10552 | + tlpx_half, ddr2ns(tlpx_half), |
| 10553 | + tclk_trail, ddr2ns(tclk_trail), |
| 10554 | + tclk_zero, ddr2ns(tclk_zero)); |
| 10555 | + DSSDBG("tclk_prepare %u (%uns)\n", |
| 10556 | + tclk_prepare, ddr2ns(tclk_prepare)); |
| 10557 | + |
| 10558 | + /* program timings */ |
| 10559 | + |
| 10560 | + r = dsi_read_reg(DSI_DSIPHY_CFG0); |
| 10561 | + r = FLD_MOD(r, ths_prepare, 31, 24); |
| 10562 | + r = FLD_MOD(r, ths_prepare_ths_zero, 23, 16); |
| 10563 | + r = FLD_MOD(r, ths_trail, 15, 8); |
| 10564 | + r = FLD_MOD(r, ths_exit, 7, 0); |
| 10565 | + dsi_write_reg(DSI_DSIPHY_CFG0, r); |
| 10566 | + |
| 10567 | + r = dsi_read_reg(DSI_DSIPHY_CFG1); |
| 10568 | + r = FLD_MOD(r, tlpx_half, 22, 16); |
| 10569 | + r = FLD_MOD(r, tclk_trail, 15, 8); |
| 10570 | + r = FLD_MOD(r, tclk_zero, 7, 0); |
| 10571 | + dsi_write_reg(DSI_DSIPHY_CFG1, r); |
| 10572 | + |
| 10573 | + r = dsi_read_reg(DSI_DSIPHY_CFG2); |
| 10574 | + r = FLD_MOD(r, tclk_prepare, 7, 0); |
| 10575 | + dsi_write_reg(DSI_DSIPHY_CFG2, r); |
| 10576 | +} |
| 10577 | + |
| 10578 | + |
| 10579 | +static int dsi_complexio_init(struct omap_dss_device *dssdev) |
| 10580 | +{ |
| 10581 | + int r = 0; |
| 10582 | + |
| 10583 | + DSSDBG("dsi_complexio_init\n"); |
| 10584 | + |
| 10585 | + /* CIO_CLK_ICG, enable L3 clk to CIO */ |
| 10586 | + REG_FLD_MOD(DSI_CLK_CTRL, 1, 14, 14); |
| 10587 | + |
| 10588 | + /* A dummy read using the SCP interface to any DSIPHY register is |
| 10589 | + * required after DSIPHY reset to complete the reset of the DSI complex |
| 10590 | + * I/O. */ |
| 10591 | + dsi_read_reg(DSI_DSIPHY_CFG5); |
| 10592 | + |
| 10593 | + if (wait_for_bit_change(DSI_DSIPHY_CFG5, 30, 1) != 1) { |
| 10594 | + DSSERR("ComplexIO PHY not coming out of reset.\n"); |
| 10595 | + r = -ENODEV; |
| 10596 | + goto err; |
| 10597 | + } |
| 10598 | + |
| 10599 | + dsi_complexio_config(dssdev); |
| 10600 | + |
| 10601 | + r = dsi_complexio_power(DSI_COMPLEXIO_POWER_ON); |
| 10602 | + |
| 10603 | + if (r) |
| 10604 | + goto err; |
| 10605 | + |
| 10606 | + if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 29, 1) != 1) { |
| 10607 | + DSSERR("ComplexIO not coming out of reset.\n"); |
| 10608 | + r = -ENODEV; |
| 10609 | + goto err; |
| 10610 | + } |
| 10611 | + |
| 10612 | + if (wait_for_bit_change(DSI_COMPLEXIO_CFG1, 21, 1) != 1) { |
| 10613 | + DSSERR("ComplexIO LDO power down.\n"); |
| 10614 | + r = -ENODEV; |
| 10615 | + goto err; |
| 10616 | + } |
| 10617 | + |
| 10618 | + dsi_complexio_timings(); |
| 10619 | + |
| 10620 | + /* |
| 10621 | + The configuration of the DSI complex I/O (number of data lanes, |
| 10622 | + position, differential order) should not be changed while |
| 10623 | + DSS.DSI_CLK_CRTRL[20] LP_CLK_ENABLE bit is set to 1. For the |
| 10624 | + hardware to recognize a new configuration of the complex I/O (done |
| 10625 | + in DSS.DSI_COMPLEXIO_CFG1 register), it is recommended to follow |
| 10626 | + this sequence: First set the DSS.DSI_CTRL[0] IF_EN bit to 1, next |
| 10627 | + reset the DSS.DSI_CTRL[0] IF_EN to 0, then set DSS.DSI_CLK_CTRL[20] |
| 10628 | + LP_CLK_ENABLE to 1, and finally, set again the DSS.DSI_CTRL[0] IF_EN |
| 10629 | + bit to 1. If the sequence is not followed, the DSi complex I/O |
| 10630 | + configuration is undetermined. |
| 10631 | + */ |
| 10632 | + dsi_if_enable(1); |
| 10633 | + dsi_if_enable(0); |
| 10634 | + REG_FLD_MOD(DSI_CLK_CTRL, 1, 20, 20); /* LP_CLK_ENABLE */ |
| 10635 | + dsi_if_enable(1); |
| 10636 | + dsi_if_enable(0); |
| 10637 | + |
| 10638 | + DSSDBG("CIO init done\n"); |
| 10639 | +err: |
| 10640 | + return r; |
| 10641 | +} |
| 10642 | + |
| 10643 | +static void dsi_complexio_uninit(void) |
| 10644 | +{ |
| 10645 | + dsi_complexio_power(DSI_COMPLEXIO_POWER_OFF); |
| 10646 | +} |
| 10647 | + |
| 10648 | +static int _dsi_wait_reset(void) |
| 10649 | +{ |
| 10650 | + int i = 0; |
| 10651 | + |
| 10652 | + while (REG_GET(DSI_SYSSTATUS, 0, 0) == 0) { |
| 10653 | + if (i++ > 5) { |
| 10654 | + DSSERR("soft reset failed\n"); |
| 10655 | + return -ENODEV; |
| 10656 | + } |
| 10657 | + udelay(1); |
| 10658 | + } |
| 10659 | + |
| 10660 | + return 0; |
| 10661 | +} |
| 10662 | + |
| 10663 | +static int _dsi_reset(void) |
| 10664 | +{ |
| 10665 | + /* Soft reset */ |
| 10666 | + REG_FLD_MOD(DSI_SYSCONFIG, 1, 1, 1); |
| 10667 | + return _dsi_wait_reset(); |
| 10668 | +} |
| 10669 | + |
| 10670 | + |
| 10671 | +static void dsi_config_tx_fifo(enum fifo_size size1, enum fifo_size size2, |
| 10672 | + enum fifo_size size3, enum fifo_size size4) |
| 10673 | +{ |
| 10674 | + u32 r = 0; |
| 10675 | + int add = 0; |
| 10676 | + int i; |
| 10677 | + |
| 10678 | + dsi.vc[0].fifo_size = size1; |
| 10679 | + dsi.vc[1].fifo_size = size2; |
| 10680 | + dsi.vc[2].fifo_size = size3; |
| 10681 | + dsi.vc[3].fifo_size = size4; |
| 10682 | + |
| 10683 | + for (i = 0; i < 4; i++) { |
| 10684 | + u8 v; |
| 10685 | + int size = dsi.vc[i].fifo_size; |
| 10686 | + |
| 10687 | + if (add + size > 4) { |
| 10688 | + DSSERR("Illegal FIFO configuration\n"); |
| 10689 | + BUG(); |
| 10690 | + } |
| 10691 | + |
| 10692 | + v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); |
| 10693 | + r |= v << (8 * i); |
| 10694 | + /*DSSDBG("TX FIFO vc %d: size %d, add %d\n", i, size, add); */ |
| 10695 | + add += size; |
| 10696 | + } |
| 10697 | + |
| 10698 | + dsi_write_reg(DSI_TX_FIFO_VC_SIZE, r); |
| 10699 | +} |
| 10700 | + |
| 10701 | +static void dsi_config_rx_fifo(enum fifo_size size1, enum fifo_size size2, |
| 10702 | + enum fifo_size size3, enum fifo_size size4) |
| 10703 | +{ |
| 10704 | + u32 r = 0; |
| 10705 | + int add = 0; |
| 10706 | + int i; |
| 10707 | + |
| 10708 | + dsi.vc[0].fifo_size = size1; |
| 10709 | + dsi.vc[1].fifo_size = size2; |
| 10710 | + dsi.vc[2].fifo_size = size3; |
| 10711 | + dsi.vc[3].fifo_size = size4; |
| 10712 | + |
| 10713 | + for (i = 0; i < 4; i++) { |
| 10714 | + u8 v; |
| 10715 | + int size = dsi.vc[i].fifo_size; |
| 10716 | + |
| 10717 | + if (add + size > 4) { |
| 10718 | + DSSERR("Illegal FIFO configuration\n"); |
| 10719 | + BUG(); |
| 10720 | + } |
| 10721 | + |
| 10722 | + v = FLD_VAL(add, 2, 0) | FLD_VAL(size, 7, 4); |
| 10723 | + r |= v << (8 * i); |
| 10724 | + /*DSSDBG("RX FIFO vc %d: size %d, add %d\n", i, size, add); */ |
| 10725 | + add += size; |
| 10726 | + } |
| 10727 | + |
| 10728 | + dsi_write_reg(DSI_RX_FIFO_VC_SIZE, r); |
| 10729 | +} |
| 10730 | + |
| 10731 | +static int dsi_force_tx_stop_mode_io(void) |
| 10732 | +{ |
| 10733 | + u32 r; |
| 10734 | + |
| 10735 | + r = dsi_read_reg(DSI_TIMING1); |
| 10736 | + r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ |
| 10737 | + dsi_write_reg(DSI_TIMING1, r); |
| 10738 | + |
| 10739 | + if (wait_for_bit_change(DSI_TIMING1, 15, 0) != 0) { |
| 10740 | + DSSERR("TX_STOP bit not going down\n"); |
| 10741 | + return -EIO; |
| 10742 | + } |
| 10743 | + |
| 10744 | + return 0; |
| 10745 | +} |
| 10746 | + |
| 10747 | +static void dsi_vc_print_status(int channel) |
| 10748 | +{ |
| 10749 | + u32 r; |
| 10750 | + |
| 10751 | + r = dsi_read_reg(DSI_VC_CTRL(channel)); |
| 10752 | + DSSDBG("vc %d: TX_FIFO_NOT_EMPTY %d, BTA_EN %d, VC_BUSY %d, " |
| 10753 | + "TX_FIFO_FULL %d, RX_FIFO_NOT_EMPTY %d, ", |
| 10754 | + channel, |
| 10755 | + FLD_GET(r, 5, 5), |
| 10756 | + FLD_GET(r, 6, 6), |
| 10757 | + FLD_GET(r, 15, 15), |
| 10758 | + FLD_GET(r, 16, 16), |
| 10759 | + FLD_GET(r, 20, 20)); |
| 10760 | + |
| 10761 | + r = dsi_read_reg(DSI_TX_FIFO_VC_EMPTINESS); |
| 10762 | + DSSDBG("EMPTINESS %d\n", (r >> (8 * channel)) & 0xff); |
| 10763 | +} |
| 10764 | + |
| 10765 | +static int dsi_vc_enable(int channel, bool enable) |
| 10766 | +{ |
| 10767 | + if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO) |
| 10768 | + DSSDBG("dsi_vc_enable channel %d, enable %d\n", |
| 10769 | + channel, enable); |
| 10770 | + |
| 10771 | + enable = enable ? 1 : 0; |
| 10772 | + |
| 10773 | + REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 0, 0); |
| 10774 | + |
| 10775 | + if (wait_for_bit_change(DSI_VC_CTRL(channel), 0, enable) != enable) { |
| 10776 | + DSSERR("Failed to set dsi_vc_enable to %d\n", enable); |
| 10777 | + return -EIO; |
| 10778 | + } |
| 10779 | + |
| 10780 | + return 0; |
| 10781 | +} |
| 10782 | + |
| 10783 | +static void dsi_vc_initial_config(int channel) |
| 10784 | +{ |
| 10785 | + u32 r; |
| 10786 | + |
| 10787 | + DSSDBGF("%d", channel); |
| 10788 | + |
| 10789 | + r = dsi_read_reg(DSI_VC_CTRL(channel)); |
| 10790 | + |
| 10791 | + if (FLD_GET(r, 15, 15)) /* VC_BUSY */ |
| 10792 | + DSSERR("VC(%d) busy when trying to configure it!\n", |
| 10793 | + channel); |
| 10794 | + |
| 10795 | + r = FLD_MOD(r, 0, 1, 1); /* SOURCE, 0 = L4 */ |
| 10796 | + r = FLD_MOD(r, 0, 2, 2); /* BTA_SHORT_EN */ |
| 10797 | + r = FLD_MOD(r, 0, 3, 3); /* BTA_LONG_EN */ |
| 10798 | + r = FLD_MOD(r, 0, 4, 4); /* MODE, 0 = command */ |
| 10799 | + r = FLD_MOD(r, 1, 7, 7); /* CS_TX_EN */ |
| 10800 | + r = FLD_MOD(r, 1, 8, 8); /* ECC_TX_EN */ |
| 10801 | + r = FLD_MOD(r, 0, 9, 9); /* MODE_SPEED, high speed on/off */ |
| 10802 | + |
| 10803 | + r = FLD_MOD(r, 4, 29, 27); /* DMA_RX_REQ_NB = no dma */ |
| 10804 | + r = FLD_MOD(r, 4, 23, 21); /* DMA_TX_REQ_NB = no dma */ |
| 10805 | + |
| 10806 | + dsi_write_reg(DSI_VC_CTRL(channel), r); |
| 10807 | + |
| 10808 | + dsi.vc[channel].mode = DSI_VC_MODE_L4; |
| 10809 | +} |
| 10810 | + |
| 10811 | +static void dsi_vc_config_l4(int channel) |
| 10812 | +{ |
| 10813 | + if (dsi.vc[channel].mode == DSI_VC_MODE_L4) |
| 10814 | + return; |
| 10815 | + |
| 10816 | + DSSDBGF("%d", channel); |
| 10817 | + |
| 10818 | + dsi_vc_enable(channel, 0); |
| 10819 | + |
| 10820 | + if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */ |
| 10821 | + DSSERR("vc(%d) busy when trying to config for L4\n", channel); |
| 10822 | + |
| 10823 | + REG_FLD_MOD(DSI_VC_CTRL(channel), 0, 1, 1); /* SOURCE, 0 = L4 */ |
| 10824 | + |
| 10825 | + dsi_vc_enable(channel, 1); |
| 10826 | + |
| 10827 | + dsi.vc[channel].mode = DSI_VC_MODE_L4; |
| 10828 | +} |
| 10829 | + |
| 10830 | +static void dsi_vc_config_vp(int channel) |
| 10831 | +{ |
| 10832 | + if (dsi.vc[channel].mode == DSI_VC_MODE_VP) |
| 10833 | + return; |
| 10834 | + |
| 10835 | + DSSDBGF("%d", channel); |
| 10836 | + |
| 10837 | + dsi_vc_enable(channel, 0); |
| 10838 | + |
| 10839 | + if (REG_GET(DSI_VC_CTRL(channel), 15, 15)) /* VC_BUSY */ |
| 10840 | + DSSERR("vc(%d) busy when trying to config for VP\n", channel); |
| 10841 | + |
| 10842 | + REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 1, 1); /* SOURCE, 1 = video port */ |
| 10843 | + |
| 10844 | + dsi_vc_enable(channel, 1); |
| 10845 | + |
| 10846 | + dsi.vc[channel].mode = DSI_VC_MODE_VP; |
| 10847 | +} |
| 10848 | + |
| 10849 | + |
| 10850 | +static void dsi_vc_enable_hs(int channel, bool enable) |
| 10851 | +{ |
| 10852 | + DSSDBG("dsi_vc_enable_hs(%d, %d)\n", channel, enable); |
| 10853 | + |
| 10854 | + dsi_vc_enable(channel, 0); |
| 10855 | + dsi_if_enable(0); |
| 10856 | + |
| 10857 | + REG_FLD_MOD(DSI_VC_CTRL(channel), enable, 9, 9); |
| 10858 | + |
| 10859 | + dsi_vc_enable(channel, 1); |
| 10860 | + dsi_if_enable(1); |
| 10861 | + |
| 10862 | + dsi_force_tx_stop_mode_io(); |
| 10863 | +} |
| 10864 | + |
| 10865 | +static void dsi_vc_flush_long_data(int channel) |
| 10866 | +{ |
| 10867 | + while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { |
| 10868 | + u32 val; |
| 10869 | + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); |
| 10870 | + DSSDBG("\t\tb1 %#02x b2 %#02x b3 %#02x b4 %#02x\n", |
| 10871 | + (val >> 0) & 0xff, |
| 10872 | + (val >> 8) & 0xff, |
| 10873 | + (val >> 16) & 0xff, |
| 10874 | + (val >> 24) & 0xff); |
| 10875 | + } |
| 10876 | +} |
| 10877 | + |
| 10878 | +static void dsi_show_rx_ack_with_err(u16 err) |
| 10879 | +{ |
| 10880 | + DSSERR("\tACK with ERROR (%#x):\n", err); |
| 10881 | + if (err & (1 << 0)) |
| 10882 | + DSSERR("\t\tSoT Error\n"); |
| 10883 | + if (err & (1 << 1)) |
| 10884 | + DSSERR("\t\tSoT Sync Error\n"); |
| 10885 | + if (err & (1 << 2)) |
| 10886 | + DSSERR("\t\tEoT Sync Error\n"); |
| 10887 | + if (err & (1 << 3)) |
| 10888 | + DSSERR("\t\tEscape Mode Entry Command Error\n"); |
| 10889 | + if (err & (1 << 4)) |
| 10890 | + DSSERR("\t\tLP Transmit Sync Error\n"); |
| 10891 | + if (err & (1 << 5)) |
| 10892 | + DSSERR("\t\tHS Receive Timeout Error\n"); |
| 10893 | + if (err & (1 << 6)) |
| 10894 | + DSSERR("\t\tFalse Control Error\n"); |
| 10895 | + if (err & (1 << 7)) |
| 10896 | + DSSERR("\t\t(reserved7)\n"); |
| 10897 | + if (err & (1 << 8)) |
| 10898 | + DSSERR("\t\tECC Error, single-bit (corrected)\n"); |
| 10899 | + if (err & (1 << 9)) |
| 10900 | + DSSERR("\t\tECC Error, multi-bit (not corrected)\n"); |
| 10901 | + if (err & (1 << 10)) |
| 10902 | + DSSERR("\t\tChecksum Error\n"); |
| 10903 | + if (err & (1 << 11)) |
| 10904 | + DSSERR("\t\tData type not recognized\n"); |
| 10905 | + if (err & (1 << 12)) |
| 10906 | + DSSERR("\t\tInvalid VC ID\n"); |
| 10907 | + if (err & (1 << 13)) |
| 10908 | + DSSERR("\t\tInvalid Transmission Length\n"); |
| 10909 | + if (err & (1 << 14)) |
| 10910 | + DSSERR("\t\t(reserved14)\n"); |
| 10911 | + if (err & (1 << 15)) |
| 10912 | + DSSERR("\t\tDSI Protocol Violation\n"); |
| 10913 | +} |
| 10914 | + |
| 10915 | +static u16 dsi_vc_flush_receive_data(int channel) |
| 10916 | +{ |
| 10917 | + /* RX_FIFO_NOT_EMPTY */ |
| 10918 | + while (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { |
| 10919 | + u32 val; |
| 10920 | + u8 dt; |
| 10921 | + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); |
| 10922 | + DSSDBG("\trawval %#08x\n", val); |
| 10923 | + dt = FLD_GET(val, 5, 0); |
| 10924 | + if (dt == DSI_DT_RX_ACK_WITH_ERR) { |
| 10925 | + u16 err = FLD_GET(val, 23, 8); |
| 10926 | + dsi_show_rx_ack_with_err(err); |
| 10927 | + } else if (dt == DSI_DT_RX_SHORT_READ_1) { |
| 10928 | + DSSDBG("\tDCS short response, 1 byte: %#x\n", |
| 10929 | + FLD_GET(val, 23, 8)); |
| 10930 | + } else if (dt == DSI_DT_RX_SHORT_READ_2) { |
| 10931 | + DSSDBG("\tDCS short response, 2 byte: %#x\n", |
| 10932 | + FLD_GET(val, 23, 8)); |
| 10933 | + } else if (dt == DSI_DT_RX_DCS_LONG_READ) { |
| 10934 | + DSSDBG("\tDCS long response, len %d\n", |
| 10935 | + FLD_GET(val, 23, 8)); |
| 10936 | + dsi_vc_flush_long_data(channel); |
| 10937 | + } else { |
| 10938 | + DSSERR("\tunknown datatype 0x%02x\n", dt); |
| 10939 | + } |
| 10940 | + } |
| 10941 | + return 0; |
| 10942 | +} |
| 10943 | + |
| 10944 | +static int dsi_vc_send_bta(int channel) |
| 10945 | +{ |
| 10946 | + if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO && |
| 10947 | + (dsi.debug_write || dsi.debug_read)) |
| 10948 | + DSSDBG("dsi_vc_send_bta %d\n", channel); |
| 10949 | + |
| 10950 | + WARN_ON(!mutex_is_locked(&dsi.bus_lock)); |
| 10951 | + |
| 10952 | + if (REG_GET(DSI_VC_CTRL(channel), 20, 20)) { /* RX_FIFO_NOT_EMPTY */ |
| 10953 | + DSSERR("rx fifo not empty when sending BTA, dumping data:\n"); |
| 10954 | + dsi_vc_flush_receive_data(channel); |
| 10955 | + } |
| 10956 | + |
| 10957 | + REG_FLD_MOD(DSI_VC_CTRL(channel), 1, 6, 6); /* BTA_EN */ |
| 10958 | + |
| 10959 | + return 0; |
| 10960 | +} |
| 10961 | + |
| 10962 | +int dsi_vc_send_bta_sync(int channel) |
| 10963 | +{ |
| 10964 | + int r = 0; |
| 10965 | + u32 err; |
| 10966 | + |
| 10967 | + INIT_COMPLETION(dsi.bta_completion); |
| 10968 | + |
| 10969 | + dsi_vc_enable_bta_irq(channel); |
| 10970 | + |
| 10971 | + r = dsi_vc_send_bta(channel); |
| 10972 | + if (r) |
| 10973 | + goto err; |
| 10974 | + |
| 10975 | + if (wait_for_completion_timeout(&dsi.bta_completion, |
| 10976 | + msecs_to_jiffies(500)) == 0) { |
| 10977 | + DSSERR("Failed to receive BTA\n"); |
| 10978 | + r = -EIO; |
| 10979 | + goto err; |
| 10980 | + } |
| 10981 | + |
| 10982 | + err = dsi_get_errors(); |
| 10983 | + if (err) { |
| 10984 | + DSSERR("Error while sending BTA: %x\n", err); |
| 10985 | + r = -EIO; |
| 10986 | + goto err; |
| 10987 | + } |
| 10988 | +err: |
| 10989 | + dsi_vc_disable_bta_irq(channel); |
| 10990 | + |
| 10991 | + return r; |
| 10992 | +} |
| 10993 | +EXPORT_SYMBOL(dsi_vc_send_bta_sync); |
| 10994 | + |
| 10995 | +static inline void dsi_vc_write_long_header(int channel, u8 data_type, |
| 10996 | + u16 len, u8 ecc) |
| 10997 | +{ |
| 10998 | + u32 val; |
| 10999 | + u8 data_id; |
| 11000 | + |
| 11001 | + WARN_ON(!mutex_is_locked(&dsi.bus_lock)); |
| 11002 | + |
| 11003 | + /*data_id = data_type | channel << 6; */ |
| 11004 | + data_id = data_type | dsi.vc[channel].dest_per << 6; |
| 11005 | + |
| 11006 | + val = FLD_VAL(data_id, 7, 0) | FLD_VAL(len, 23, 8) | |
| 11007 | + FLD_VAL(ecc, 31, 24); |
| 11008 | + |
| 11009 | + dsi_write_reg(DSI_VC_LONG_PACKET_HEADER(channel), val); |
| 11010 | +} |
| 11011 | + |
| 11012 | +static inline void dsi_vc_write_long_payload(int channel, |
| 11013 | + u8 b1, u8 b2, u8 b3, u8 b4) |
| 11014 | +{ |
| 11015 | + u32 val; |
| 11016 | + |
| 11017 | + val = b4 << 24 | b3 << 16 | b2 << 8 | b1 << 0; |
| 11018 | + |
| 11019 | +/* DSSDBG("\twriting %02x, %02x, %02x, %02x (%#010x)\n", |
| 11020 | + b1, b2, b3, b4, val); */ |
| 11021 | + |
| 11022 | + dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(channel), val); |
| 11023 | +} |
| 11024 | + |
| 11025 | +static int dsi_vc_send_long(int channel, u8 data_type, u8 *data, u16 len, |
| 11026 | + u8 ecc) |
| 11027 | +{ |
| 11028 | + /*u32 val; */ |
| 11029 | + int i; |
| 11030 | + u8 *p; |
| 11031 | + int r = 0; |
| 11032 | + u8 b1, b2, b3, b4; |
| 11033 | + |
| 11034 | + if (dsi.debug_write) |
| 11035 | + DSSDBG("dsi_vc_send_long, %d bytes\n", len); |
| 11036 | + |
| 11037 | + /* len + header */ |
| 11038 | + if (dsi.vc[channel].fifo_size * 32 * 4 < len + 4) { |
| 11039 | + DSSERR("unable to send long packet: packet too long.\n"); |
| 11040 | + return -EINVAL; |
| 11041 | + } |
| 11042 | + |
| 11043 | + dsi_vc_config_l4(channel); |
| 11044 | + |
| 11045 | + dsi_vc_write_long_header(channel, data_type, len, ecc); |
| 11046 | + |
| 11047 | + /*dsi_vc_print_status(0); */ |
| 11048 | + |
| 11049 | + p = data; |
| 11050 | + for (i = 0; i < len >> 2; i++) { |
| 11051 | + if (dsi.debug_write) |
| 11052 | + DSSDBG("\tsending full packet %d\n", i); |
| 11053 | + /*dsi_vc_print_status(0); */ |
| 11054 | + |
| 11055 | + b1 = *p++; |
| 11056 | + b2 = *p++; |
| 11057 | + b3 = *p++; |
| 11058 | + b4 = *p++; |
| 11059 | + |
| 11060 | + dsi_vc_write_long_payload(channel, b1, b2, b3, b4); |
| 11061 | + } |
| 11062 | + |
| 11063 | + i = len % 4; |
| 11064 | + if (i) { |
| 11065 | + b1 = 0; b2 = 0; b3 = 0; |
| 11066 | + |
| 11067 | + if (dsi.debug_write) |
| 11068 | + DSSDBG("\tsending remainder bytes %d\n", i); |
| 11069 | + |
| 11070 | + switch (i) { |
| 11071 | + case 3: |
| 11072 | + b1 = *p++; |
| 11073 | + b2 = *p++; |
| 11074 | + b3 = *p++; |
| 11075 | + break; |
| 11076 | + case 2: |
| 11077 | + b1 = *p++; |
| 11078 | + b2 = *p++; |
| 11079 | + break; |
| 11080 | + case 1: |
| 11081 | + b1 = *p++; |
| 11082 | + break; |
| 11083 | + } |
| 11084 | + |
| 11085 | + dsi_vc_write_long_payload(channel, b1, b2, b3, 0); |
| 11086 | + } |
| 11087 | + |
| 11088 | + return r; |
| 11089 | +} |
| 11090 | + |
| 11091 | +static int dsi_vc_send_short(int channel, u8 data_type, u16 data, u8 ecc) |
| 11092 | +{ |
| 11093 | + u32 r; |
| 11094 | + u8 data_id; |
| 11095 | + |
| 11096 | + WARN_ON(!mutex_is_locked(&dsi.bus_lock)); |
| 11097 | + |
| 11098 | + if (dsi.debug_write) |
| 11099 | + DSSDBG("dsi_vc_send_short(ch%d, dt %#x, b1 %#x, b2 %#x)\n", |
| 11100 | + channel, |
| 11101 | + data_type, data & 0xff, (data >> 8) & 0xff); |
| 11102 | + |
| 11103 | + dsi_vc_config_l4(channel); |
| 11104 | + |
| 11105 | + if (FLD_GET(dsi_read_reg(DSI_VC_CTRL(channel)), 16, 16)) { |
| 11106 | + DSSERR("ERROR FIFO FULL, aborting transfer\n"); |
| 11107 | + return -EINVAL; |
| 11108 | + } |
| 11109 | + |
| 11110 | + data_id = data_type | channel << 6; |
| 11111 | + |
| 11112 | + r = (data_id << 0) | (data << 8) | (ecc << 24); |
| 11113 | + |
| 11114 | + dsi_write_reg(DSI_VC_SHORT_PACKET_HEADER(channel), r); |
| 11115 | + |
| 11116 | + return 0; |
| 11117 | +} |
| 11118 | + |
| 11119 | +int dsi_vc_send_null(int channel) |
| 11120 | +{ |
| 11121 | + u8 nullpkg[] = {0, 0, 0, 0}; |
| 11122 | + return dsi_vc_send_long(0, DSI_DT_NULL_PACKET, nullpkg, 4, 0); |
| 11123 | +} |
| 11124 | +EXPORT_SYMBOL(dsi_vc_send_null); |
| 11125 | + |
| 11126 | +int dsi_vc_dcs_write_nosync(int channel, u8 *data, int len) |
| 11127 | +{ |
| 11128 | + int r; |
| 11129 | + |
| 11130 | + BUG_ON(len == 0); |
| 11131 | + |
| 11132 | + if (len == 1) { |
| 11133 | + r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_0, |
| 11134 | + data[0], 0); |
| 11135 | + } else if (len == 2) { |
| 11136 | + r = dsi_vc_send_short(channel, DSI_DT_DCS_SHORT_WRITE_1, |
| 11137 | + data[0] | (data[1] << 8), 0); |
| 11138 | + } else { |
| 11139 | + /* 0x39 = DCS Long Write */ |
| 11140 | + r = dsi_vc_send_long(channel, DSI_DT_DCS_LONG_WRITE, |
| 11141 | + data, len, 0); |
| 11142 | + } |
| 11143 | + |
| 11144 | + return r; |
| 11145 | +} |
| 11146 | +EXPORT_SYMBOL(dsi_vc_dcs_write_nosync); |
| 11147 | + |
| 11148 | +int dsi_vc_dcs_write(int channel, u8 *data, int len) |
| 11149 | +{ |
| 11150 | + int r; |
| 11151 | + |
| 11152 | + r = dsi_vc_dcs_write_nosync(channel, data, len); |
| 11153 | + if (r) |
| 11154 | + return r; |
| 11155 | + |
| 11156 | + r = dsi_vc_send_bta_sync(channel); |
| 11157 | + |
| 11158 | + return r; |
| 11159 | +} |
| 11160 | +EXPORT_SYMBOL(dsi_vc_dcs_write); |
| 11161 | + |
| 11162 | +int dsi_vc_dcs_read(int channel, u8 dcs_cmd, u8 *buf, int buflen) |
| 11163 | +{ |
| 11164 | + u32 val; |
| 11165 | + u8 dt; |
| 11166 | + int r; |
| 11167 | + |
| 11168 | + if (dsi.debug_read) |
| 11169 | + DSSDBG("dsi_vc_dcs_read(ch%d, dcs_cmd %u)\n", channel, dcs_cmd); |
| 11170 | + |
| 11171 | + r = dsi_vc_send_short(channel, DSI_DT_DCS_READ, dcs_cmd, 0); |
| 11172 | + if (r) |
| 11173 | + return r; |
| 11174 | + |
| 11175 | + r = dsi_vc_send_bta_sync(channel); |
| 11176 | + if (r) |
| 11177 | + return r; |
| 11178 | + |
| 11179 | + /* RX_FIFO_NOT_EMPTY */ |
| 11180 | + if (REG_GET(DSI_VC_CTRL(channel), 20, 20) == 0) { |
| 11181 | + DSSERR("RX fifo empty when trying to read.\n"); |
| 11182 | + return -EIO; |
| 11183 | + } |
| 11184 | + |
| 11185 | + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); |
| 11186 | + if (dsi.debug_read) |
| 11187 | + DSSDBG("\theader: %08x\n", val); |
| 11188 | + dt = FLD_GET(val, 5, 0); |
| 11189 | + if (dt == DSI_DT_RX_ACK_WITH_ERR) { |
| 11190 | + u16 err = FLD_GET(val, 23, 8); |
| 11191 | + dsi_show_rx_ack_with_err(err); |
| 11192 | + return -EIO; |
| 11193 | + |
| 11194 | + } else if (dt == DSI_DT_RX_SHORT_READ_1) { |
| 11195 | + u8 data = FLD_GET(val, 15, 8); |
| 11196 | + if (dsi.debug_read) |
| 11197 | + DSSDBG("\tDCS short response, 1 byte: %02x\n", data); |
| 11198 | + |
| 11199 | + if (buflen < 1) |
| 11200 | + return -EIO; |
| 11201 | + |
| 11202 | + buf[0] = data; |
| 11203 | + |
| 11204 | + return 1; |
| 11205 | + } else if (dt == DSI_DT_RX_SHORT_READ_2) { |
| 11206 | + u16 data = FLD_GET(val, 23, 8); |
| 11207 | + if (dsi.debug_read) |
| 11208 | + DSSDBG("\tDCS short response, 2 byte: %04x\n", data); |
| 11209 | + |
| 11210 | + if (buflen < 2) |
| 11211 | + return -EIO; |
| 11212 | + |
| 11213 | + buf[0] = data & 0xff; |
| 11214 | + buf[1] = (data >> 8) & 0xff; |
| 11215 | + |
| 11216 | + return 2; |
| 11217 | + } else if (dt == DSI_DT_RX_DCS_LONG_READ) { |
| 11218 | + int w; |
| 11219 | + int len = FLD_GET(val, 23, 8); |
| 11220 | + if (dsi.debug_read) |
| 11221 | + DSSDBG("\tDCS long response, len %d\n", len); |
| 11222 | + |
| 11223 | + if (len > buflen) |
| 11224 | + return -EIO; |
| 11225 | + |
| 11226 | + /* two byte checksum ends the packet, not included in len */ |
| 11227 | + for (w = 0; w < len + 2;) { |
| 11228 | + int b; |
| 11229 | + val = dsi_read_reg(DSI_VC_SHORT_PACKET_HEADER(channel)); |
| 11230 | + if (dsi.debug_read) |
| 11231 | + DSSDBG("\t\t%02x %02x %02x %02x\n", |
| 11232 | + (val >> 0) & 0xff, |
| 11233 | + (val >> 8) & 0xff, |
| 11234 | + (val >> 16) & 0xff, |
| 11235 | + (val >> 24) & 0xff); |
| 11236 | + |
| 11237 | + for (b = 0; b < 4; ++b) { |
| 11238 | + if (w < len) |
| 11239 | + buf[w] = (val >> (b * 8)) & 0xff; |
| 11240 | + /* we discard the 2 byte checksum */ |
| 11241 | + ++w; |
| 11242 | + } |
| 11243 | + } |
| 11244 | + |
| 11245 | + return len; |
| 11246 | + |
| 11247 | + } else { |
| 11248 | + DSSERR("\tunknown datatype 0x%02x\n", dt); |
| 11249 | + return -EIO; |
| 11250 | + } |
| 11251 | +} |
| 11252 | +EXPORT_SYMBOL(dsi_vc_dcs_read); |
| 11253 | + |
| 11254 | + |
| 11255 | +int dsi_vc_set_max_rx_packet_size(int channel, u16 len) |
| 11256 | +{ |
| 11257 | + return dsi_vc_send_short(channel, DSI_DT_SET_MAX_RET_PKG_SIZE, |
| 11258 | + len, 0); |
| 11259 | +} |
| 11260 | +EXPORT_SYMBOL(dsi_vc_set_max_rx_packet_size); |
| 11261 | + |
| 11262 | + |
| 11263 | +static int dsi_set_lp_rx_timeout(int ns, int x4, int x16) |
| 11264 | +{ |
| 11265 | + u32 r; |
| 11266 | + unsigned long fck; |
| 11267 | + int ticks; |
| 11268 | + |
| 11269 | + /* ticks in DSI_FCK */ |
| 11270 | + |
| 11271 | + fck = dsi_fclk_rate(); |
| 11272 | + ticks = (fck / 1000 / 1000) * ns / 1000; |
| 11273 | + |
| 11274 | + if (ticks > 0x1fff) { |
| 11275 | + DSSERR("LP_TX_TO too high\n"); |
| 11276 | + return -EINVAL; |
| 11277 | + } |
| 11278 | + |
| 11279 | + r = dsi_read_reg(DSI_TIMING2); |
| 11280 | + r = FLD_MOD(r, 1, 15, 15); /* LP_RX_TO */ |
| 11281 | + r = FLD_MOD(r, x16, 14, 14); /* LP_RX_TO_X16 */ |
| 11282 | + r = FLD_MOD(r, x4, 13, 13); /* LP_RX_TO_X4 */ |
| 11283 | + r = FLD_MOD(r, ticks, 12, 0); /* LP_RX_COUNTER */ |
| 11284 | + dsi_write_reg(DSI_TIMING2, r); |
| 11285 | + |
| 11286 | + DSSDBG("LP_RX_TO %ld ns (%#x ticks)\n", |
| 11287 | + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / |
| 11288 | + (fck / 1000 / 1000), |
| 11289 | + ticks); |
| 11290 | + |
| 11291 | + return 0; |
| 11292 | +} |
| 11293 | + |
| 11294 | +static int dsi_set_ta_timeout(int ns, int x8, int x16) |
| 11295 | +{ |
| 11296 | + u32 r; |
| 11297 | + unsigned long fck; |
| 11298 | + int ticks; |
| 11299 | + |
| 11300 | + /* ticks in DSI_FCK */ |
| 11301 | + |
| 11302 | + fck = dsi_fclk_rate(); |
| 11303 | + ticks = (fck / 1000 / 1000) * ns / 1000; |
| 11304 | + |
| 11305 | + if (ticks > 0x1fff) { |
| 11306 | + DSSERR("TA_TO too high\n"); |
| 11307 | + return -EINVAL; |
| 11308 | + } |
| 11309 | + |
| 11310 | + r = dsi_read_reg(DSI_TIMING1); |
| 11311 | + r = FLD_MOD(r, 1, 31, 31); /* TA_TO */ |
| 11312 | + r = FLD_MOD(r, x16, 30, 30); /* TA_TO_X16 */ |
| 11313 | + r = FLD_MOD(r, x8, 29, 29); /* TA_TO_X8 */ |
| 11314 | + r = FLD_MOD(r, ticks, 28, 16); /* TA_TO_COUNTER */ |
| 11315 | + dsi_write_reg(DSI_TIMING1, r); |
| 11316 | + |
| 11317 | + DSSDBG("TA_TO %ld ns (%#x ticks)\n", |
| 11318 | + (ticks * (x16 ? 16 : 1) * (x8 ? 8 : 1) * 1000) / |
| 11319 | + (fck / 1000 / 1000), |
| 11320 | + ticks); |
| 11321 | + |
| 11322 | + return 0; |
| 11323 | +} |
| 11324 | + |
| 11325 | +static int dsi_set_stop_state_counter(int ns, int x4, int x16) |
| 11326 | +{ |
| 11327 | + u32 r; |
| 11328 | + unsigned long fck; |
| 11329 | + int ticks; |
| 11330 | + |
| 11331 | + /* ticks in DSI_FCK */ |
| 11332 | + |
| 11333 | + fck = dsi_fclk_rate(); |
| 11334 | + ticks = (fck / 1000 / 1000) * ns / 1000; |
| 11335 | + |
| 11336 | + if (ticks > 0x1fff) { |
| 11337 | + DSSERR("STOP_STATE_COUNTER_IO too high\n"); |
| 11338 | + return -EINVAL; |
| 11339 | + } |
| 11340 | + |
| 11341 | + r = dsi_read_reg(DSI_TIMING1); |
| 11342 | + r = FLD_MOD(r, 1, 15, 15); /* FORCE_TX_STOP_MODE_IO */ |
| 11343 | + r = FLD_MOD(r, x16, 14, 14); /* STOP_STATE_X16_IO */ |
| 11344 | + r = FLD_MOD(r, x4, 13, 13); /* STOP_STATE_X4_IO */ |
| 11345 | + r = FLD_MOD(r, ticks, 12, 0); /* STOP_STATE_COUNTER_IO */ |
| 11346 | + dsi_write_reg(DSI_TIMING1, r); |
| 11347 | + |
| 11348 | + DSSDBG("STOP_STATE_COUNTER %ld ns (%#x ticks)\n", |
| 11349 | + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / |
| 11350 | + (fck / 1000 / 1000), |
| 11351 | + ticks); |
| 11352 | + |
| 11353 | + return 0; |
| 11354 | +} |
| 11355 | + |
| 11356 | +static int dsi_set_hs_tx_timeout(int ns, int x4, int x16) |
| 11357 | +{ |
| 11358 | + u32 r; |
| 11359 | + unsigned long fck; |
| 11360 | + int ticks; |
| 11361 | + |
| 11362 | + /* ticks in TxByteClkHS */ |
| 11363 | + |
| 11364 | + fck = dsi.ddr_clk / 4; |
| 11365 | + ticks = (fck / 1000 / 1000) * ns / 1000; |
| 11366 | + |
| 11367 | + if (ticks > 0x1fff) { |
| 11368 | + DSSERR("HS_TX_TO too high\n"); |
| 11369 | + return -EINVAL; |
| 11370 | + } |
| 11371 | + |
| 11372 | + r = dsi_read_reg(DSI_TIMING2); |
| 11373 | + r = FLD_MOD(r, 1, 31, 31); /* HS_TX_TO */ |
| 11374 | + r = FLD_MOD(r, x16, 30, 30); /* HS_TX_TO_X16 */ |
| 11375 | + r = FLD_MOD(r, x4, 29, 29); /* HS_TX_TO_X8 (4 really) */ |
| 11376 | + r = FLD_MOD(r, ticks, 28, 16); /* HS_TX_TO_COUNTER */ |
| 11377 | + dsi_write_reg(DSI_TIMING2, r); |
| 11378 | + |
| 11379 | + DSSDBG("HS_TX_TO %ld ns (%#x ticks)\n", |
| 11380 | + (ticks * (x16 ? 16 : 1) * (x4 ? 4 : 1) * 1000) / |
| 11381 | + (fck / 1000 / 1000), |
| 11382 | + ticks); |
| 11383 | + |
| 11384 | + return 0; |
| 11385 | +} |
| 11386 | +static int dsi_proto_config(struct omap_dss_device *dssdev) |
| 11387 | +{ |
| 11388 | + u32 r; |
| 11389 | + int buswidth = 0; |
| 11390 | + int div; |
| 11391 | + |
| 11392 | + dsi_config_tx_fifo(DSI_FIFO_SIZE_128, |
| 11393 | + DSI_FIFO_SIZE_0, |
| 11394 | + DSI_FIFO_SIZE_0, |
| 11395 | + DSI_FIFO_SIZE_0); |
| 11396 | + |
| 11397 | + dsi_config_rx_fifo(DSI_FIFO_SIZE_128, |
| 11398 | + DSI_FIFO_SIZE_0, |
| 11399 | + DSI_FIFO_SIZE_0, |
| 11400 | + DSI_FIFO_SIZE_0); |
| 11401 | + |
| 11402 | + /* XXX what values for the timeouts? */ |
| 11403 | + dsi_set_stop_state_counter(1000, 0, 0); |
| 11404 | + |
| 11405 | + dsi_set_ta_timeout(50000, 1, 1); |
| 11406 | + |
| 11407 | + /* 3000ns * 16 */ |
| 11408 | + dsi_set_lp_rx_timeout(3000, 0, 1); |
| 11409 | + |
| 11410 | + /* 10000ns * 4 */ |
| 11411 | + dsi_set_hs_tx_timeout(10000, 1, 0); |
| 11412 | + |
| 11413 | + switch (dssdev->ctrl.pixel_size) { |
| 11414 | + case 16: |
| 11415 | + buswidth = 0; |
| 11416 | + break; |
| 11417 | + case 18: |
| 11418 | + buswidth = 1; |
| 11419 | + break; |
| 11420 | + case 24: |
| 11421 | + buswidth = 2; |
| 11422 | + break; |
| 11423 | + default: |
| 11424 | + BUG(); |
| 11425 | + } |
| 11426 | + |
| 11427 | + r = dsi_read_reg(DSI_CTRL); |
| 11428 | + r = FLD_MOD(r, 1, 1, 1); /* CS_RX_EN */ |
| 11429 | + r = FLD_MOD(r, 1, 2, 2); /* ECC_RX_EN */ |
| 11430 | + r = FLD_MOD(r, 1, 3, 3); /* TX_FIFO_ARBITRATION */ |
| 11431 | + |
| 11432 | + div = dispc_lclk_rate() / dispc_pclk_rate(); |
| 11433 | + r = FLD_MOD(r, div == 2 ? 0 : 1, 4, 4); /* VP_CLK_RATIO */ |
| 11434 | + r = FLD_MOD(r, buswidth, 7, 6); /* VP_DATA_BUS_WIDTH */ |
| 11435 | + r = FLD_MOD(r, 0, 8, 8); /* VP_CLK_POL */ |
| 11436 | + r = FLD_MOD(r, 2, 13, 12); /* LINE_BUFFER, 2 lines */ |
| 11437 | + r = FLD_MOD(r, 1, 14, 14); /* TRIGGER_RESET_MODE */ |
| 11438 | + r = FLD_MOD(r, 1, 19, 19); /* EOT_ENABLE */ |
| 11439 | + r = FLD_MOD(r, 1, 24, 24); /* DCS_CMD_ENABLE */ |
| 11440 | + r = FLD_MOD(r, 0, 25, 25); /* DCS_CMD_CODE, 1=start, 0=continue */ |
| 11441 | + |
| 11442 | + dsi_write_reg(DSI_CTRL, r); |
| 11443 | + |
| 11444 | + dsi_vc_initial_config(0); |
| 11445 | + |
| 11446 | + /* set all vc targets to peripheral 0 */ |
| 11447 | + dsi.vc[0].dest_per = 0; |
| 11448 | + dsi.vc[1].dest_per = 0; |
| 11449 | + dsi.vc[2].dest_per = 0; |
| 11450 | + dsi.vc[3].dest_per = 0; |
| 11451 | + |
| 11452 | + return 0; |
| 11453 | +} |
| 11454 | + |
| 11455 | +static void dsi_proto_timings(struct omap_dss_device *dssdev) |
| 11456 | +{ |
| 11457 | + unsigned tlpx, tclk_zero, tclk_prepare, tclk_trail; |
| 11458 | + unsigned tclk_pre, tclk_post; |
| 11459 | + unsigned ths_prepare, ths_prepare_ths_zero, ths_zero; |
| 11460 | + unsigned ths_trail, ths_exit; |
| 11461 | + unsigned ddr_clk_pre, ddr_clk_post; |
| 11462 | + unsigned enter_hs_mode_lat, exit_hs_mode_lat; |
| 11463 | + unsigned ths_eot; |
| 11464 | + u32 r; |
| 11465 | + |
| 11466 | + r = dsi_read_reg(DSI_DSIPHY_CFG0); |
| 11467 | + ths_prepare = FLD_GET(r, 31, 24); |
| 11468 | + ths_prepare_ths_zero = FLD_GET(r, 23, 16); |
| 11469 | + ths_zero = ths_prepare_ths_zero - ths_prepare; |
| 11470 | + ths_trail = FLD_GET(r, 15, 8); |
| 11471 | + ths_exit = FLD_GET(r, 7, 0); |
| 11472 | + |
| 11473 | + r = dsi_read_reg(DSI_DSIPHY_CFG1); |
| 11474 | + tlpx = FLD_GET(r, 22, 16) * 2; |
| 11475 | + tclk_trail = FLD_GET(r, 15, 8); |
| 11476 | + tclk_zero = FLD_GET(r, 7, 0); |
| 11477 | + |
| 11478 | + r = dsi_read_reg(DSI_DSIPHY_CFG2); |
| 11479 | + tclk_prepare = FLD_GET(r, 7, 0); |
| 11480 | + |
| 11481 | + /* min 8*UI */ |
| 11482 | + tclk_pre = 20; |
| 11483 | + /* min 60ns + 52*UI */ |
| 11484 | + tclk_post = ns2ddr(60) + 26; |
| 11485 | + |
| 11486 | + /* ths_eot is 2 for 2 datalanes and 4 for 1 datalane */ |
| 11487 | + if (dssdev->phy.dsi.data1_lane != 0 && |
| 11488 | + dssdev->phy.dsi.data2_lane != 0) |
| 11489 | + ths_eot = 2; |
| 11490 | + else |
| 11491 | + ths_eot = 4; |
| 11492 | + |
| 11493 | + ddr_clk_pre = DIV_ROUND_UP(tclk_pre + tlpx + tclk_zero + tclk_prepare, |
| 11494 | + 4); |
| 11495 | + ddr_clk_post = DIV_ROUND_UP(tclk_post + tclk_trail, 4) + ths_eot; |
| 11496 | + |
| 11497 | + BUG_ON(ddr_clk_pre == 0 || ddr_clk_pre > 255); |
| 11498 | + BUG_ON(ddr_clk_post == 0 || ddr_clk_post > 255); |
| 11499 | + |
| 11500 | + r = dsi_read_reg(DSI_CLK_TIMING); |
| 11501 | + r = FLD_MOD(r, ddr_clk_pre, 15, 8); |
| 11502 | + r = FLD_MOD(r, ddr_clk_post, 7, 0); |
| 11503 | + dsi_write_reg(DSI_CLK_TIMING, r); |
| 11504 | + |
| 11505 | + DSSDBG("ddr_clk_pre %u, ddr_clk_post %u\n", |
| 11506 | + ddr_clk_pre, |
| 11507 | + ddr_clk_post); |
| 11508 | + |
| 11509 | + enter_hs_mode_lat = 1 + DIV_ROUND_UP(tlpx, 4) + |
| 11510 | + DIV_ROUND_UP(ths_prepare, 4) + |
| 11511 | + DIV_ROUND_UP(ths_zero + 3, 4); |
| 11512 | + |
| 11513 | + exit_hs_mode_lat = DIV_ROUND_UP(ths_trail + ths_exit, 4) + 1 + ths_eot; |
| 11514 | + |
| 11515 | + r = FLD_VAL(enter_hs_mode_lat, 31, 16) | |
| 11516 | + FLD_VAL(exit_hs_mode_lat, 15, 0); |
| 11517 | + dsi_write_reg(DSI_VM_TIMING7, r); |
| 11518 | + |
| 11519 | + DSSDBG("enter_hs_mode_lat %u, exit_hs_mode_lat %u\n", |
| 11520 | + enter_hs_mode_lat, exit_hs_mode_lat); |
| 11521 | +} |
| 11522 | + |
| 11523 | + |
| 11524 | +#define DSI_DECL_VARS \ |
| 11525 | + int __dsi_cb = 0; u32 __dsi_cv = 0; |
| 11526 | + |
| 11527 | +#define DSI_FLUSH(ch) \ |
| 11528 | + if (__dsi_cb > 0) { \ |
| 11529 | + /*DSSDBG("sending long packet %#010x\n", __dsi_cv);*/ \ |
| 11530 | + dsi_write_reg(DSI_VC_LONG_PACKET_PAYLOAD(ch), __dsi_cv); \ |
| 11531 | + __dsi_cb = __dsi_cv = 0; \ |
| 11532 | + } |
| 11533 | + |
| 11534 | +#define DSI_PUSH(ch, data) \ |
| 11535 | + do { \ |
| 11536 | + __dsi_cv |= (data) << (__dsi_cb * 8); \ |
| 11537 | + /*DSSDBG("cv = %#010x, cb = %d\n", __dsi_cv, __dsi_cb);*/ \ |
| 11538 | + if (++__dsi_cb > 3) \ |
| 11539 | + DSI_FLUSH(ch); \ |
| 11540 | + } while (0) |
| 11541 | + |
| 11542 | +static int dsi_update_screen_l4(struct omap_dss_device *dssdev, |
| 11543 | + int x, int y, int w, int h) |
| 11544 | +{ |
| 11545 | + /* Note: supports only 24bit colors in 32bit container */ |
| 11546 | + int first = 1; |
| 11547 | + int fifo_stalls = 0; |
| 11548 | + int max_dsi_packet_size; |
| 11549 | + int max_data_per_packet; |
| 11550 | + int max_pixels_per_packet; |
| 11551 | + int pixels_left; |
| 11552 | + int bytespp = dssdev->ctrl.pixel_size / 8; |
| 11553 | + int scr_width; |
| 11554 | + u32 __iomem *data; |
| 11555 | + int start_offset; |
| 11556 | + int horiz_inc; |
| 11557 | + int current_x; |
| 11558 | + struct omap_overlay *ovl; |
| 11559 | + |
| 11560 | + debug_irq = 0; |
| 11561 | + |
| 11562 | + DSSDBG("dsi_update_screen_l4 (%d,%d %dx%d)\n", |
| 11563 | + x, y, w, h); |
| 11564 | + |
| 11565 | + ovl = dssdev->manager->overlays[0]; |
| 11566 | + |
| 11567 | + if (ovl->info.color_mode != OMAP_DSS_COLOR_RGB24U) |
| 11568 | + return -EINVAL; |
| 11569 | + |
| 11570 | + if (dssdev->ctrl.pixel_size != 24) |
| 11571 | + return -EINVAL; |
| 11572 | + |
| 11573 | + scr_width = ovl->info.screen_width; |
| 11574 | + data = ovl->info.vaddr; |
| 11575 | + |
| 11576 | + start_offset = scr_width * y + x; |
| 11577 | + horiz_inc = scr_width - w; |
| 11578 | + current_x = x; |
| 11579 | + |
| 11580 | + /* We need header(4) + DCSCMD(1) + pixels(numpix*bytespp) bytes |
| 11581 | + * in fifo */ |
| 11582 | + |
| 11583 | + /* When using CPU, max long packet size is TX buffer size */ |
| 11584 | + max_dsi_packet_size = dsi.vc[0].fifo_size * 32 * 4; |
| 11585 | + |
| 11586 | + /* we seem to get better perf if we divide the tx fifo to half, |
| 11587 | + and while the other half is being sent, we fill the other half |
| 11588 | + max_dsi_packet_size /= 2; */ |
| 11589 | + |
| 11590 | + max_data_per_packet = max_dsi_packet_size - 4 - 1; |
| 11591 | + |
| 11592 | + max_pixels_per_packet = max_data_per_packet / bytespp; |
| 11593 | + |
| 11594 | + DSSDBG("max_pixels_per_packet %d\n", max_pixels_per_packet); |
| 11595 | + |
| 11596 | + pixels_left = w * h; |
| 11597 | + |
| 11598 | + DSSDBG("total pixels %d\n", pixels_left); |
| 11599 | + |
| 11600 | + data += start_offset; |
| 11601 | + |
| 11602 | + while (pixels_left > 0) { |
| 11603 | + /* 0x2c = write_memory_start */ |
| 11604 | + /* 0x3c = write_memory_continue */ |
| 11605 | + u8 dcs_cmd = first ? 0x2c : 0x3c; |
| 11606 | + int pixels; |
| 11607 | + DSI_DECL_VARS; |
| 11608 | + first = 0; |
| 11609 | + |
| 11610 | +#if 1 |
| 11611 | + /* using fifo not empty */ |
| 11612 | + /* TX_FIFO_NOT_EMPTY */ |
| 11613 | + while (FLD_GET(dsi_read_reg(DSI_VC_CTRL(0)), 5, 5)) { |
| 11614 | + udelay(1); |
| 11615 | + fifo_stalls++; |
| 11616 | + if (fifo_stalls > 0xfffff) { |
| 11617 | + DSSERR("fifo stalls overflow, pixels left %d\n", |
| 11618 | + pixels_left); |
| 11619 | + dsi_if_enable(0); |
| 11620 | + return -EIO; |
| 11621 | + } |
| 11622 | + } |
| 11623 | +#elif 1 |
| 11624 | + /* using fifo emptiness */ |
| 11625 | + while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 < |
| 11626 | + max_dsi_packet_size) { |
| 11627 | + fifo_stalls++; |
| 11628 | + if (fifo_stalls > 0xfffff) { |
| 11629 | + DSSERR("fifo stalls overflow, pixels left %d\n", |
| 11630 | + pixels_left); |
| 11631 | + dsi_if_enable(0); |
| 11632 | + return -EIO; |
| 11633 | + } |
| 11634 | + } |
| 11635 | +#else |
| 11636 | + while ((REG_GET(DSI_TX_FIFO_VC_EMPTINESS, 7, 0)+1)*4 == 0) { |
| 11637 | + fifo_stalls++; |
| 11638 | + if (fifo_stalls > 0xfffff) { |
| 11639 | + DSSERR("fifo stalls overflow, pixels left %d\n", |
| 11640 | + pixels_left); |
| 11641 | + dsi_if_enable(0); |
| 11642 | + return -EIO; |
| 11643 | + } |
| 11644 | + } |
| 11645 | +#endif |
| 11646 | + pixels = min(max_pixels_per_packet, pixels_left); |
| 11647 | + |
| 11648 | + pixels_left -= pixels; |
| 11649 | + |
| 11650 | + dsi_vc_write_long_header(0, DSI_DT_DCS_LONG_WRITE, |
| 11651 | + 1 + pixels * bytespp, 0); |
| 11652 | + |
| 11653 | + DSI_PUSH(0, dcs_cmd); |
| 11654 | + |
| 11655 | + while (pixels-- > 0) { |
| 11656 | + u32 pix = __raw_readl(data++); |
| 11657 | + |
| 11658 | + DSI_PUSH(0, (pix >> 16) & 0xff); |
| 11659 | + DSI_PUSH(0, (pix >> 8) & 0xff); |
| 11660 | + DSI_PUSH(0, (pix >> 0) & 0xff); |
| 11661 | + |
| 11662 | + current_x++; |
| 11663 | + if (current_x == x+w) { |
| 11664 | + current_x = x; |
| 11665 | + data += horiz_inc; |
| 11666 | + } |
| 11667 | + } |
| 11668 | + |
| 11669 | + DSI_FLUSH(0); |
| 11670 | + } |
| 11671 | + |
| 11672 | + return 0; |
| 11673 | +} |
| 11674 | + |
| 11675 | +static void dsi_update_screen_dispc(struct omap_dss_device *dssdev, |
| 11676 | + u16 x, u16 y, u16 w, u16 h) |
| 11677 | +{ |
| 11678 | + int bytespp = dssdev->ctrl.pixel_size / 8; |
| 11679 | + int len; |
| 11680 | + int total_len; |
| 11681 | + int packet_payload; |
| 11682 | + int packet_len; |
| 11683 | + u32 l; |
| 11684 | + bool use_te_trigger; |
| 11685 | + const int channel = 0; |
| 11686 | + |
| 11687 | + use_te_trigger = dsi.te_enabled && !dsi.use_ext_te; |
| 11688 | + |
| 11689 | + if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO) |
| 11690 | + DSSDBG("dsi_update_screen_dispc(%d,%d %dx%d)\n", |
| 11691 | + x, y, w, h); |
| 11692 | + |
| 11693 | + len = w * h * bytespp; |
| 11694 | + |
| 11695 | + /* XXX: one packet could be longer, I think? Line buffer is |
| 11696 | + * 1024 x 24bits, but we have to put DCS cmd there also. |
| 11697 | + * 1023 * 3 should work, but causes strange color effects. */ |
| 11698 | + packet_payload = min(w, (u16)1020) * bytespp; |
| 11699 | + |
| 11700 | + packet_len = packet_payload + 1; /* 1 byte for DCS cmd */ |
| 11701 | + total_len = (len / packet_payload) * packet_len; |
| 11702 | + |
| 11703 | + if (len % packet_payload) |
| 11704 | + total_len += (len % packet_payload) + 1; |
| 11705 | + |
| 11706 | + if (0) |
| 11707 | + dsi_vc_print_status(1); |
| 11708 | + |
| 11709 | + l = FLD_VAL(total_len, 23, 0); /* TE_SIZE */ |
| 11710 | + dsi_write_reg(DSI_VC_TE(channel), l); |
| 11711 | + |
| 11712 | + dsi_vc_write_long_header(channel, DSI_DT_DCS_LONG_WRITE, packet_len, 0); |
| 11713 | + |
| 11714 | + if (use_te_trigger) |
| 11715 | + l = FLD_MOD(l, 1, 30, 30); /* TE_EN */ |
| 11716 | + else |
| 11717 | + l = FLD_MOD(l, 1, 31, 31); /* TE_START */ |
| 11718 | + dsi_write_reg(DSI_VC_TE(channel), l); |
| 11719 | + |
| 11720 | + /* We put SIDLEMODE to no-idle for the duration of the transfer, |
| 11721 | + * because DSS interrupts are not capable of waking up the CPU and the |
| 11722 | + * framedone interrupt could be delayed for quite a long time. I think |
| 11723 | + * the same goes for any DSS interrupts, but for some reason I have not |
| 11724 | + * seen the problem anywhere else than here. |
| 11725 | + */ |
| 11726 | + dispc_disable_sidle(); |
| 11727 | + |
| 11728 | + dss_start_update(dssdev); |
| 11729 | + |
| 11730 | + if (use_te_trigger) { |
| 11731 | + /* disable LP_RX_TO, so that we can receive TE. Time to wait |
| 11732 | + * for TE is longer than the timer allows */ |
| 11733 | + REG_FLD_MOD(DSI_TIMING2, 0, 15, 15); /* LP_RX_TO */ |
| 11734 | + |
| 11735 | + dsi_vc_send_bta(channel); |
| 11736 | + } |
| 11737 | +} |
| 11738 | + |
| 11739 | +static void dsi_framedone_irq_callback(void *data, u32 mask) |
| 11740 | +{ |
| 11741 | + /* Note: We get FRAMEDONE when DISPC has finished sending pixels and |
| 11742 | + * turns itself off. However, DSI still has the pixels in its buffers, |
| 11743 | + * and is sending the data. |
| 11744 | + */ |
| 11745 | + |
| 11746 | + /* SIDLEMODE back to smart-idle */ |
| 11747 | + dispc_enable_sidle(); |
| 11748 | + |
| 11749 | + dsi.framedone_received = true; |
| 11750 | + wake_up(&dsi.waitqueue); |
| 11751 | +} |
| 11752 | + |
| 11753 | +static void dsi_set_update_region(struct omap_dss_device *dssdev, |
| 11754 | + u16 x, u16 y, u16 w, u16 h) |
| 11755 | +{ |
| 11756 | + spin_lock(&dsi.update_lock); |
| 11757 | + if (dsi.update_region.dirty) { |
| 11758 | + dsi.update_region.x = min(x, dsi.update_region.x); |
| 11759 | + dsi.update_region.y = min(y, dsi.update_region.y); |
| 11760 | + dsi.update_region.w = max(w, dsi.update_region.w); |
| 11761 | + dsi.update_region.h = max(h, dsi.update_region.h); |
| 11762 | + } else { |
| 11763 | + dsi.update_region.x = x; |
| 11764 | + dsi.update_region.y = y; |
| 11765 | + dsi.update_region.w = w; |
| 11766 | + dsi.update_region.h = h; |
| 11767 | + } |
| 11768 | + |
| 11769 | + dsi.update_region.device = dssdev; |
| 11770 | + dsi.update_region.dirty = true; |
| 11771 | + |
| 11772 | + spin_unlock(&dsi.update_lock); |
| 11773 | + |
| 11774 | +} |
| 11775 | + |
| 11776 | +static void dsi_start_auto_update(struct omap_dss_device *dssdev) |
| 11777 | +{ |
| 11778 | + u16 w, h; |
| 11779 | + int i; |
| 11780 | + |
| 11781 | + DSSDBG("starting auto update\n"); |
| 11782 | + |
| 11783 | + /* In automatic mode the overlay settings are applied like on DPI/SDI. |
| 11784 | + * Mark the overlays dirty, so that we get the overlays configured, as |
| 11785 | + * manual mode has left them in bad shape after config partia planes */ |
| 11786 | + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { |
| 11787 | + struct omap_overlay *ovl; |
| 11788 | + ovl = omap_dss_get_overlay(i); |
| 11789 | + if (ovl->manager == dssdev->manager) |
| 11790 | + ovl->info_dirty = true; |
| 11791 | + } |
| 11792 | + dssdev->manager->apply(dssdev->manager); |
| 11793 | + |
| 11794 | + dssdev->get_resolution(dssdev, &w, &h); |
| 11795 | + |
| 11796 | + dsi_set_update_region(dssdev, 0, 0, w, h); |
| 11797 | + |
| 11798 | + dsi_perf_mark_start_auto(); |
| 11799 | + |
| 11800 | + wake_up(&dsi.waitqueue); |
| 11801 | +} |
| 11802 | + |
| 11803 | +static int dsi_set_te(struct omap_dss_device *dssdev, bool enable) |
| 11804 | +{ |
| 11805 | + int r; |
| 11806 | + r = dssdev->driver->enable_te(dssdev, enable); |
| 11807 | + /* XXX for some reason, DSI TE breaks if we don't wait here. |
| 11808 | + * Panel bug? Needs more studying */ |
| 11809 | + msleep(100); |
| 11810 | + return r; |
| 11811 | +} |
| 11812 | + |
| 11813 | +static void dsi_handle_framedone(void) |
| 11814 | +{ |
| 11815 | + int r; |
| 11816 | + const int channel = 0; |
| 11817 | + bool use_te_trigger; |
| 11818 | + |
| 11819 | + use_te_trigger = dsi.te_enabled && !dsi.use_ext_te; |
| 11820 | + |
| 11821 | + if (dsi.update_mode != OMAP_DSS_UPDATE_AUTO) |
| 11822 | + DSSDBG("FRAMEDONE\n"); |
| 11823 | + |
| 11824 | + if (use_te_trigger) { |
| 11825 | + /* enable LP_RX_TO again after the TE */ |
| 11826 | + REG_FLD_MOD(DSI_TIMING2, 1, 15, 15); /* LP_RX_TO */ |
| 11827 | + } |
| 11828 | + |
| 11829 | + /* Send BTA after the frame. We need this for the TE to work, as TE |
| 11830 | + * trigger is only sent for BTAs without preceding packet. Thus we need |
| 11831 | + * to BTA after the pixel packets so that next BTA will cause TE |
| 11832 | + * trigger. |
| 11833 | + * |
| 11834 | + * This is not needed when TE is not in use, but we do it anyway to |
| 11835 | + * make sure that the transfer has been completed. It would be more |
| 11836 | + * optimal, but more complex, to wait only just before starting next |
| 11837 | + * transfer. */ |
| 11838 | + r = dsi_vc_send_bta_sync(channel); |
| 11839 | + if (r) |
| 11840 | + DSSERR("BTA after framedone failed\n"); |
| 11841 | + |
| 11842 | +#ifdef CONFIG_OMAP2_DSS_FAKE_VSYNC |
| 11843 | + dispc_fake_vsync_irq(); |
| 11844 | +#endif |
| 11845 | +} |
| 11846 | + |
| 11847 | +static int dsi_update_thread(void *data) |
| 11848 | +{ |
| 11849 | + unsigned long timeout; |
| 11850 | + struct omap_dss_device *device; |
| 11851 | + u16 x, y, w, h; |
| 11852 | + |
| 11853 | + while (1) { |
| 11854 | + bool sched; |
| 11855 | + |
| 11856 | + wait_event_interruptible(dsi.waitqueue, |
| 11857 | + dsi.update_mode == OMAP_DSS_UPDATE_AUTO || |
| 11858 | + (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL && |
| 11859 | + dsi.update_region.dirty == true) || |
| 11860 | + kthread_should_stop()); |
| 11861 | + |
| 11862 | + if (kthread_should_stop()) |
| 11863 | + break; |
| 11864 | + |
| 11865 | + dsi_bus_lock(); |
| 11866 | + |
| 11867 | + if (dsi.update_mode == OMAP_DSS_UPDATE_DISABLED || |
| 11868 | + kthread_should_stop()) { |
| 11869 | + dsi_bus_unlock(); |
| 11870 | + break; |
| 11871 | + } |
| 11872 | + |
| 11873 | + dsi_perf_mark_setup(); |
| 11874 | + |
| 11875 | + if (dsi.update_region.dirty) { |
| 11876 | + spin_lock(&dsi.update_lock); |
| 11877 | + dsi.active_update_region = dsi.update_region; |
| 11878 | + dsi.update_region.dirty = false; |
| 11879 | + spin_unlock(&dsi.update_lock); |
| 11880 | + } |
| 11881 | + |
| 11882 | + device = dsi.active_update_region.device; |
| 11883 | + x = dsi.active_update_region.x; |
| 11884 | + y = dsi.active_update_region.y; |
| 11885 | + w = dsi.active_update_region.w; |
| 11886 | + h = dsi.active_update_region.h; |
| 11887 | + |
| 11888 | + if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { |
| 11889 | + |
| 11890 | + if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL) { |
| 11891 | + dss_setup_partial_planes(device, |
| 11892 | + &x, &y, &w, &h); |
| 11893 | +#if 1 |
| 11894 | + /* XXX there seems to be a bug in this driver |
| 11895 | + * or OMAP hardware. Some updates with certain |
| 11896 | + * widths and x coordinates fail. These widths |
| 11897 | + * are always odd, so "fix" it here for now */ |
| 11898 | + if (w & 1) { |
| 11899 | + u16 dw, dh; |
| 11900 | + device->get_resolution(device, |
| 11901 | + &dw, &dh); |
| 11902 | + if (x + w == dw) |
| 11903 | + x &= ~1; |
| 11904 | + ++w; |
| 11905 | + |
| 11906 | + dss_setup_partial_planes(device, |
| 11907 | + &x, &y, &w, &h); |
| 11908 | + } |
| 11909 | +#endif |
| 11910 | + } |
| 11911 | + |
| 11912 | + dispc_set_lcd_size(w, h); |
| 11913 | + } |
| 11914 | + |
| 11915 | + if (dsi.active_update_region.dirty) { |
| 11916 | + dsi.active_update_region.dirty = false; |
| 11917 | + /* XXX TODO we don't need to send the coords, if they |
| 11918 | + * are the same that are already programmed to the |
| 11919 | + * panel. That should speed up manual update a bit */ |
| 11920 | + device->driver->setup_update(device, x, y, w, h); |
| 11921 | + } |
| 11922 | + |
| 11923 | + dsi_perf_mark_start(); |
| 11924 | + |
| 11925 | + if (device->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { |
| 11926 | + dsi_vc_config_vp(0); |
| 11927 | + |
| 11928 | + if (dsi.te_enabled && dsi.use_ext_te) |
| 11929 | + device->driver->wait_for_te(device); |
| 11930 | + |
| 11931 | + dsi.framedone_received = false; |
| 11932 | + |
| 11933 | + dsi_update_screen_dispc(device, x, y, w, h); |
| 11934 | + |
| 11935 | + /* wait for framedone */ |
| 11936 | + timeout = msecs_to_jiffies(1000); |
| 11937 | + timeout = wait_event_timeout(dsi.waitqueue, |
| 11938 | + dsi.framedone_received == true, |
| 11939 | + timeout); |
| 11940 | + |
| 11941 | + if (timeout == 0) { |
| 11942 | + DSSERR("framedone timeout\n"); |
| 11943 | + DSSERR("failed update %d,%d %dx%d\n", |
| 11944 | + x, y, w, h); |
| 11945 | + |
| 11946 | + dispc_enable_sidle(); |
| 11947 | + dispc_enable_lcd_out(0); |
| 11948 | + } else { |
| 11949 | + dsi_handle_framedone(); |
| 11950 | + dsi_perf_show("DISPC"); |
| 11951 | + } |
| 11952 | + } else { |
| 11953 | + dsi_update_screen_l4(device, x, y, w, h); |
| 11954 | + dsi_perf_show("L4"); |
| 11955 | + } |
| 11956 | + |
| 11957 | + sched = atomic_read(&dsi.bus_lock.count) < 0; |
| 11958 | + |
| 11959 | + complete_all(&dsi.update_completion); |
| 11960 | + |
| 11961 | + dsi_bus_unlock(); |
| 11962 | + |
| 11963 | + /* XXX We need to give others chance to get the bus lock. Is |
| 11964 | + * there a better way for this? */ |
| 11965 | + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO && sched) |
| 11966 | + schedule_timeout_interruptible(1); |
| 11967 | + } |
| 11968 | + |
| 11969 | + DSSDBG("update thread exiting\n"); |
| 11970 | + |
| 11971 | + return 0; |
| 11972 | +} |
| 11973 | + |
| 11974 | + |
| 11975 | + |
| 11976 | +/* Display funcs */ |
| 11977 | + |
| 11978 | +static int dsi_display_init_dispc(struct omap_dss_device *dssdev) |
| 11979 | +{ |
| 11980 | + int r; |
| 11981 | + |
| 11982 | + r = omap_dispc_register_isr(dsi_framedone_irq_callback, NULL, |
| 11983 | + DISPC_IRQ_FRAMEDONE); |
| 11984 | + if (r) { |
| 11985 | + DSSERR("can't get FRAMEDONE irq\n"); |
| 11986 | + return r; |
| 11987 | + } |
| 11988 | + |
| 11989 | + dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); |
| 11990 | + |
| 11991 | + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_DSI); |
| 11992 | + dispc_enable_fifohandcheck(1); |
| 11993 | + |
| 11994 | + dispc_set_tft_data_lines(dssdev->ctrl.pixel_size); |
| 11995 | + |
| 11996 | + { |
| 11997 | + struct omap_video_timings timings = { |
| 11998 | + .hsw = 1, |
| 11999 | + .hfp = 1, |
| 12000 | + .hbp = 1, |
| 12001 | + .vsw = 1, |
| 12002 | + .vfp = 0, |
| 12003 | + .vbp = 0, |
| 12004 | + }; |
| 12005 | + |
| 12006 | + dispc_set_lcd_timings(&timings); |
| 12007 | + } |
| 12008 | + |
| 12009 | + return 0; |
| 12010 | +} |
| 12011 | + |
| 12012 | +static void dsi_display_uninit_dispc(struct omap_dss_device *dssdev) |
| 12013 | +{ |
| 12014 | + omap_dispc_unregister_isr(dsi_framedone_irq_callback, NULL, |
| 12015 | + DISPC_IRQ_FRAMEDONE); |
| 12016 | +} |
| 12017 | + |
| 12018 | +static int dsi_display_init_dsi(struct omap_dss_device *dssdev) |
| 12019 | +{ |
| 12020 | + struct dsi_clock_info cinfo; |
| 12021 | + int r; |
| 12022 | + |
| 12023 | + _dsi_print_reset_status(); |
| 12024 | + |
| 12025 | + r = dsi_pll_init(1, 0); |
| 12026 | + if (r) |
| 12027 | + goto err0; |
| 12028 | + |
| 12029 | + r = dsi_pll_calc_ddrfreq(dssdev->phy.dsi.ddr_clk_hz, &cinfo); |
| 12030 | + if (r) |
| 12031 | + goto err1; |
| 12032 | + |
| 12033 | + r = dsi_pll_program(&cinfo); |
| 12034 | + if (r) |
| 12035 | + goto err1; |
| 12036 | + |
| 12037 | + DSSDBG("PLL OK\n"); |
| 12038 | + |
| 12039 | + r = dsi_complexio_init(dssdev); |
| 12040 | + if (r) |
| 12041 | + goto err1; |
| 12042 | + |
| 12043 | + _dsi_print_reset_status(); |
| 12044 | + |
| 12045 | + dsi_proto_timings(dssdev); |
| 12046 | + dsi_set_lp_clk_divisor(dssdev); |
| 12047 | + |
| 12048 | + if (1) |
| 12049 | + _dsi_print_reset_status(); |
| 12050 | + |
| 12051 | + r = dsi_proto_config(dssdev); |
| 12052 | + if (r) |
| 12053 | + goto err2; |
| 12054 | + |
| 12055 | + /* enable interface */ |
| 12056 | + dsi_vc_enable(0, 1); |
| 12057 | + dsi_if_enable(1); |
| 12058 | + dsi_force_tx_stop_mode_io(); |
| 12059 | + |
| 12060 | + if (dssdev->driver->enable) { |
| 12061 | + r = dssdev->driver->enable(dssdev); |
| 12062 | + if (r) |
| 12063 | + goto err3; |
| 12064 | + } |
| 12065 | + |
| 12066 | + /* enable high-speed after initial config */ |
| 12067 | + dsi_vc_enable_hs(0, 1); |
| 12068 | + |
| 12069 | + return 0; |
| 12070 | +err3: |
| 12071 | + dsi_if_enable(0); |
| 12072 | +err2: |
| 12073 | + dsi_complexio_uninit(); |
| 12074 | +err1: |
| 12075 | + dsi_pll_uninit(); |
| 12076 | +err0: |
| 12077 | + return r; |
| 12078 | +} |
| 12079 | + |
| 12080 | +static void dsi_display_uninit_dsi(struct omap_dss_device *dssdev) |
| 12081 | +{ |
| 12082 | + if (dssdev->driver->disable) |
| 12083 | + dssdev->driver->disable(dssdev); |
| 12084 | + |
| 12085 | + dsi_complexio_uninit(); |
| 12086 | + dsi_pll_uninit(); |
| 12087 | +} |
| 12088 | + |
| 12089 | +static int dsi_core_init(void) |
| 12090 | +{ |
| 12091 | + /* Autoidle */ |
| 12092 | + REG_FLD_MOD(DSI_SYSCONFIG, 1, 0, 0); |
| 12093 | + |
| 12094 | + /* ENWAKEUP */ |
| 12095 | + REG_FLD_MOD(DSI_SYSCONFIG, 1, 2, 2); |
| 12096 | + |
| 12097 | + /* SIDLEMODE smart-idle */ |
| 12098 | + REG_FLD_MOD(DSI_SYSCONFIG, 2, 4, 3); |
| 12099 | + |
| 12100 | + _dsi_initialize_irq(); |
| 12101 | + |
| 12102 | + return 0; |
| 12103 | +} |
| 12104 | + |
| 12105 | +static int dsi_display_enable(struct omap_dss_device *dssdev) |
| 12106 | +{ |
| 12107 | + int r = 0; |
| 12108 | + |
| 12109 | + DSSDBG("dsi_display_enable\n"); |
| 12110 | + |
| 12111 | + mutex_lock(&dsi.lock); |
| 12112 | + dsi_bus_lock(); |
| 12113 | + |
| 12114 | + r = omap_dss_start_device(dssdev); |
| 12115 | + if (r) { |
| 12116 | + DSSERR("failed to start device\n"); |
| 12117 | + goto err0; |
| 12118 | + } |
| 12119 | + |
| 12120 | + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { |
| 12121 | + DSSERR("dssdev already enabled\n"); |
| 12122 | + r = -EINVAL; |
| 12123 | + goto err1; |
| 12124 | + } |
| 12125 | + |
| 12126 | + enable_clocks(1); |
| 12127 | + dsi_enable_pll_clock(1); |
| 12128 | + |
| 12129 | + r = _dsi_reset(); |
| 12130 | + if (r) |
| 12131 | + goto err2; |
| 12132 | + |
| 12133 | + dsi_core_init(); |
| 12134 | + |
| 12135 | + r = dsi_display_init_dispc(dssdev); |
| 12136 | + if (r) |
| 12137 | + goto err2; |
| 12138 | + |
| 12139 | + r = dsi_display_init_dsi(dssdev); |
| 12140 | + if (r) |
| 12141 | + goto err3; |
| 12142 | + |
| 12143 | + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; |
| 12144 | + |
| 12145 | + dsi.use_ext_te = dssdev->phy.dsi.ext_te; |
| 12146 | + r = dsi_set_te(dssdev, dsi.te_enabled); |
| 12147 | + if (r) |
| 12148 | + goto err3; |
| 12149 | + |
| 12150 | + dsi.update_mode = dsi.user_update_mode; |
| 12151 | + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) |
| 12152 | + dsi_start_auto_update(dssdev); |
| 12153 | + |
| 12154 | + dsi_bus_unlock(); |
| 12155 | + mutex_unlock(&dsi.lock); |
| 12156 | + |
| 12157 | + return 0; |
| 12158 | + |
| 12159 | +err3: |
| 12160 | + dsi_display_uninit_dispc(dssdev); |
| 12161 | +err2: |
| 12162 | + enable_clocks(0); |
| 12163 | + dsi_enable_pll_clock(0); |
| 12164 | +err1: |
| 12165 | + omap_dss_stop_device(dssdev); |
| 12166 | +err0: |
| 12167 | + dsi_bus_unlock(); |
| 12168 | + mutex_unlock(&dsi.lock); |
| 12169 | + DSSDBG("dsi_display_enable FAILED\n"); |
| 12170 | + return r; |
| 12171 | +} |
| 12172 | + |
| 12173 | +static void dsi_display_disable(struct omap_dss_device *dssdev) |
| 12174 | +{ |
| 12175 | + DSSDBG("dsi_display_disable\n"); |
| 12176 | + |
| 12177 | + mutex_lock(&dsi.lock); |
| 12178 | + dsi_bus_lock(); |
| 12179 | + |
| 12180 | + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED || |
| 12181 | + dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) |
| 12182 | + goto end; |
| 12183 | + |
| 12184 | + dsi.update_mode = OMAP_DSS_UPDATE_DISABLED; |
| 12185 | + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; |
| 12186 | + |
| 12187 | + dsi_display_uninit_dispc(dssdev); |
| 12188 | + |
| 12189 | + dsi_display_uninit_dsi(dssdev); |
| 12190 | + |
| 12191 | + enable_clocks(0); |
| 12192 | + dsi_enable_pll_clock(0); |
| 12193 | + |
| 12194 | + omap_dss_stop_device(dssdev); |
| 12195 | +end: |
| 12196 | + dsi_bus_unlock(); |
| 12197 | + mutex_unlock(&dsi.lock); |
| 12198 | +} |
| 12199 | + |
| 12200 | +static int dsi_display_suspend(struct omap_dss_device *dssdev) |
| 12201 | +{ |
| 12202 | + DSSDBG("dsi_display_suspend\n"); |
| 12203 | + |
| 12204 | + mutex_lock(&dsi.lock); |
| 12205 | + dsi_bus_lock(); |
| 12206 | + |
| 12207 | + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED || |
| 12208 | + dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) |
| 12209 | + goto end; |
| 12210 | + |
| 12211 | + dsi.update_mode = OMAP_DSS_UPDATE_DISABLED; |
| 12212 | + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; |
| 12213 | + |
| 12214 | + dsi_display_uninit_dispc(dssdev); |
| 12215 | + |
| 12216 | + dsi_display_uninit_dsi(dssdev); |
| 12217 | + |
| 12218 | + enable_clocks(0); |
| 12219 | + dsi_enable_pll_clock(0); |
| 12220 | +end: |
| 12221 | + dsi_bus_unlock(); |
| 12222 | + mutex_unlock(&dsi.lock); |
| 12223 | + |
| 12224 | + return 0; |
| 12225 | +} |
| 12226 | + |
| 12227 | +static int dsi_display_resume(struct omap_dss_device *dssdev) |
| 12228 | +{ |
| 12229 | + int r; |
| 12230 | + |
| 12231 | + DSSDBG("dsi_display_resume\n"); |
| 12232 | + |
| 12233 | + mutex_lock(&dsi.lock); |
| 12234 | + dsi_bus_lock(); |
| 12235 | + |
| 12236 | + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { |
| 12237 | + DSSERR("dssdev not suspended\n"); |
| 12238 | + r = -EINVAL; |
| 12239 | + goto err0; |
| 12240 | + } |
| 12241 | + |
| 12242 | + enable_clocks(1); |
| 12243 | + dsi_enable_pll_clock(1); |
| 12244 | + |
| 12245 | + r = _dsi_reset(); |
| 12246 | + if (r) |
| 12247 | + goto err1; |
| 12248 | + |
| 12249 | + dsi_core_init(); |
| 12250 | + |
| 12251 | + r = dsi_display_init_dispc(dssdev); |
| 12252 | + if (r) |
| 12253 | + goto err1; |
| 12254 | + |
| 12255 | + r = dsi_display_init_dsi(dssdev); |
| 12256 | + if (r) |
| 12257 | + goto err2; |
| 12258 | + |
| 12259 | + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; |
| 12260 | + |
| 12261 | + r = dsi_set_te(dssdev, dsi.te_enabled); |
| 12262 | + if (r) |
| 12263 | + goto err2; |
| 12264 | + |
| 12265 | + dsi.update_mode = dsi.user_update_mode; |
| 12266 | + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) |
| 12267 | + dsi_start_auto_update(dssdev); |
| 12268 | + |
| 12269 | + dsi_bus_unlock(); |
| 12270 | + mutex_unlock(&dsi.lock); |
| 12271 | + |
| 12272 | + return 0; |
| 12273 | + |
| 12274 | +err2: |
| 12275 | + dsi_display_uninit_dispc(dssdev); |
| 12276 | +err1: |
| 12277 | + enable_clocks(0); |
| 12278 | + dsi_enable_pll_clock(0); |
| 12279 | +err0: |
| 12280 | + dsi_bus_unlock(); |
| 12281 | + mutex_unlock(&dsi.lock); |
| 12282 | + DSSDBG("dsi_display_resume FAILED\n"); |
| 12283 | + return r; |
| 12284 | +} |
| 12285 | + |
| 12286 | +static int dsi_display_update(struct omap_dss_device *dssdev, |
| 12287 | + u16 x, u16 y, u16 w, u16 h) |
| 12288 | +{ |
| 12289 | + int r = 0; |
| 12290 | + u16 dw, dh; |
| 12291 | + |
| 12292 | + DSSDBG("dsi_display_update(%d,%d %dx%d)\n", x, y, w, h); |
| 12293 | + |
| 12294 | + mutex_lock(&dsi.lock); |
| 12295 | + |
| 12296 | + if (dsi.update_mode != OMAP_DSS_UPDATE_MANUAL) |
| 12297 | + goto end; |
| 12298 | + |
| 12299 | + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) |
| 12300 | + goto end; |
| 12301 | + |
| 12302 | + dssdev->get_resolution(dssdev, &dw, &dh); |
| 12303 | + |
| 12304 | + if (x > dw || y > dh) |
| 12305 | + goto end; |
| 12306 | + |
| 12307 | + if (x + w > dw) |
| 12308 | + w = dw - x; |
| 12309 | + |
| 12310 | + if (y + h > dh) |
| 12311 | + h = dh - y; |
| 12312 | + |
| 12313 | + if (w == 0 || h == 0) |
| 12314 | + goto end; |
| 12315 | + |
| 12316 | + dsi_set_update_region(dssdev, x, y, w, h); |
| 12317 | + |
| 12318 | + wake_up(&dsi.waitqueue); |
| 12319 | + |
| 12320 | +end: |
| 12321 | + mutex_unlock(&dsi.lock); |
| 12322 | + |
| 12323 | + return r; |
| 12324 | +} |
| 12325 | + |
| 12326 | +static int dsi_display_sync(struct omap_dss_device *dssdev) |
| 12327 | +{ |
| 12328 | + bool wait; |
| 12329 | + |
| 12330 | + DSSDBG("dsi_display_sync()\n"); |
| 12331 | + |
| 12332 | + mutex_lock(&dsi.lock); |
| 12333 | + dsi_bus_lock(); |
| 12334 | + |
| 12335 | + if (dsi.update_mode == OMAP_DSS_UPDATE_MANUAL && |
| 12336 | + dsi.update_region.dirty) { |
| 12337 | + INIT_COMPLETION(dsi.update_completion); |
| 12338 | + wait = true; |
| 12339 | + } else { |
| 12340 | + wait = false; |
| 12341 | + } |
| 12342 | + |
| 12343 | + dsi_bus_unlock(); |
| 12344 | + mutex_unlock(&dsi.lock); |
| 12345 | + |
| 12346 | + if (wait) |
| 12347 | + wait_for_completion_interruptible(&dsi.update_completion); |
| 12348 | + |
| 12349 | + DSSDBG("dsi_display_sync() done\n"); |
| 12350 | + return 0; |
| 12351 | +} |
| 12352 | + |
| 12353 | +static int dsi_display_set_update_mode(struct omap_dss_device *dssdev, |
| 12354 | + enum omap_dss_update_mode mode) |
| 12355 | +{ |
| 12356 | + DSSDBGF("%d", mode); |
| 12357 | + |
| 12358 | + mutex_lock(&dsi.lock); |
| 12359 | + dsi_bus_lock(); |
| 12360 | + |
| 12361 | + if (dsi.update_mode != mode) { |
| 12362 | + dsi.user_update_mode = mode; |
| 12363 | + dsi.update_mode = mode; |
| 12364 | + |
| 12365 | + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE && |
| 12366 | + mode == OMAP_DSS_UPDATE_AUTO) |
| 12367 | + dsi_start_auto_update(dssdev); |
| 12368 | + } |
| 12369 | + |
| 12370 | + dsi_bus_unlock(); |
| 12371 | + mutex_unlock(&dsi.lock); |
| 12372 | + |
| 12373 | + return 0; |
| 12374 | +} |
| 12375 | + |
| 12376 | +static enum omap_dss_update_mode dsi_display_get_update_mode( |
| 12377 | + struct omap_dss_device *dssdev) |
| 12378 | +{ |
| 12379 | + return dsi.update_mode; |
| 12380 | +} |
| 12381 | + |
| 12382 | + |
| 12383 | +static int dsi_display_enable_te(struct omap_dss_device *dssdev, bool enable) |
| 12384 | +{ |
| 12385 | + int r = 0; |
| 12386 | + |
| 12387 | + DSSDBGF("%d", enable); |
| 12388 | + |
| 12389 | + if (!dssdev->driver->enable_te) |
| 12390 | + return -ENOENT; |
| 12391 | + |
| 12392 | + dsi_bus_lock(); |
| 12393 | + |
| 12394 | + dsi.te_enabled = enable; |
| 12395 | + |
| 12396 | + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) |
| 12397 | + goto end; |
| 12398 | + |
| 12399 | + r = dsi_set_te(dssdev, enable); |
| 12400 | +end: |
| 12401 | + dsi_bus_unlock(); |
| 12402 | + |
| 12403 | + return r; |
| 12404 | +} |
| 12405 | + |
| 12406 | +static int dsi_display_get_te(struct omap_dss_device *dssdev) |
| 12407 | +{ |
| 12408 | + return dsi.te_enabled; |
| 12409 | +} |
| 12410 | + |
| 12411 | +static int dsi_display_set_rotate(struct omap_dss_device *dssdev, u8 rotate) |
| 12412 | +{ |
| 12413 | + |
| 12414 | + DSSDBGF("%d", rotate); |
| 12415 | + |
| 12416 | + if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate) |
| 12417 | + return -EINVAL; |
| 12418 | + |
| 12419 | + dsi_bus_lock(); |
| 12420 | + dssdev->driver->set_rotate(dssdev, rotate); |
| 12421 | + if (dsi.update_mode == OMAP_DSS_UPDATE_AUTO) { |
| 12422 | + u16 w, h; |
| 12423 | + /* the display dimensions may have changed, so set a new |
| 12424 | + * update region */ |
| 12425 | + dssdev->get_resolution(dssdev, &w, &h); |
| 12426 | + dsi_set_update_region(dssdev, 0, 0, w, h); |
| 12427 | + } |
| 12428 | + dsi_bus_unlock(); |
| 12429 | + |
| 12430 | + return 0; |
| 12431 | +} |
| 12432 | + |
| 12433 | +static u8 dsi_display_get_rotate(struct omap_dss_device *dssdev) |
| 12434 | +{ |
| 12435 | + if (!dssdev->driver->set_rotate || !dssdev->driver->get_rotate) |
| 12436 | + return 0; |
| 12437 | + |
| 12438 | + return dssdev->driver->get_rotate(dssdev); |
| 12439 | +} |
| 12440 | + |
| 12441 | +static int dsi_display_set_mirror(struct omap_dss_device *dssdev, bool mirror) |
| 12442 | +{ |
| 12443 | + DSSDBGF("%d", mirror); |
| 12444 | + |
| 12445 | + if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror) |
| 12446 | + return -EINVAL; |
| 12447 | + |
| 12448 | + dsi_bus_lock(); |
| 12449 | + dssdev->driver->set_mirror(dssdev, mirror); |
| 12450 | + dsi_bus_unlock(); |
| 12451 | + |
| 12452 | + return 0; |
| 12453 | +} |
| 12454 | + |
| 12455 | +static bool dsi_display_get_mirror(struct omap_dss_device *dssdev) |
| 12456 | +{ |
| 12457 | + if (!dssdev->driver->set_mirror || !dssdev->driver->get_mirror) |
| 12458 | + return 0; |
| 12459 | + |
| 12460 | + return dssdev->driver->get_mirror(dssdev); |
| 12461 | +} |
| 12462 | + |
| 12463 | +static int dsi_display_run_test(struct omap_dss_device *dssdev, int test_num) |
| 12464 | +{ |
| 12465 | + int r; |
| 12466 | + |
| 12467 | + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) |
| 12468 | + return -EIO; |
| 12469 | + |
| 12470 | + DSSDBGF("%d", test_num); |
| 12471 | + |
| 12472 | + dsi_bus_lock(); |
| 12473 | + |
| 12474 | + /* run test first in low speed mode */ |
| 12475 | + dsi_vc_enable_hs(0, 0); |
| 12476 | + |
| 12477 | + if (dssdev->driver->run_test) { |
| 12478 | + r = dssdev->driver->run_test(dssdev, test_num); |
| 12479 | + if (r) |
| 12480 | + goto end; |
| 12481 | + } |
| 12482 | + |
| 12483 | + /* then in high speed */ |
| 12484 | + dsi_vc_enable_hs(0, 1); |
| 12485 | + |
| 12486 | + if (dssdev->driver->run_test) { |
| 12487 | + r = dssdev->driver->run_test(dssdev, test_num); |
| 12488 | + if (r) |
| 12489 | + goto end; |
| 12490 | + } |
| 12491 | + |
| 12492 | +end: |
| 12493 | + dsi_vc_enable_hs(0, 1); |
| 12494 | + |
| 12495 | + dsi_bus_unlock(); |
| 12496 | + |
| 12497 | + return r; |
| 12498 | +} |
| 12499 | + |
| 12500 | +static int dsi_display_memory_read(struct omap_dss_device *dssdev, |
| 12501 | + void *buf, size_t size, |
| 12502 | + u16 x, u16 y, u16 w, u16 h) |
| 12503 | +{ |
| 12504 | + int r; |
| 12505 | + |
| 12506 | + DSSDBGF(""); |
| 12507 | + |
| 12508 | + if (!dssdev->driver->memory_read) |
| 12509 | + return -EINVAL; |
| 12510 | + |
| 12511 | + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) |
| 12512 | + return -EIO; |
| 12513 | + |
| 12514 | + dsi_bus_lock(); |
| 12515 | + |
| 12516 | + r = dssdev->driver->memory_read(dssdev, buf, size, |
| 12517 | + x, y, w, h); |
| 12518 | + |
| 12519 | + dsi_bus_unlock(); |
| 12520 | + |
| 12521 | + return r; |
| 12522 | +} |
| 12523 | + |
| 12524 | +void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, |
| 12525 | + u32 fifo_size, enum omap_burst_size *burst_size, |
| 12526 | + u32 *fifo_low, u32 *fifo_high) |
| 12527 | +{ |
| 12528 | + unsigned burst_size_bytes; |
| 12529 | + |
| 12530 | + *burst_size = OMAP_DSS_BURST_16x32; |
| 12531 | + burst_size_bytes = 16 * 32 / 8; |
| 12532 | + |
| 12533 | + *fifo_high = fifo_size - burst_size_bytes; |
| 12534 | + *fifo_low = 0; |
| 12535 | +} |
| 12536 | + |
| 12537 | +int dsi_init_display(struct omap_dss_device *dssdev) |
| 12538 | +{ |
| 12539 | + DSSDBG("DSI init\n"); |
| 12540 | + |
| 12541 | + dssdev->enable = dsi_display_enable; |
| 12542 | + dssdev->disable = dsi_display_disable; |
| 12543 | + dssdev->suspend = dsi_display_suspend; |
| 12544 | + dssdev->resume = dsi_display_resume; |
| 12545 | + dssdev->update = dsi_display_update; |
| 12546 | + dssdev->sync = dsi_display_sync; |
| 12547 | + dssdev->set_update_mode = dsi_display_set_update_mode; |
| 12548 | + dssdev->get_update_mode = dsi_display_get_update_mode; |
| 12549 | + dssdev->enable_te = dsi_display_enable_te; |
| 12550 | + dssdev->get_te = dsi_display_get_te; |
| 12551 | + |
| 12552 | + dssdev->get_rotate = dsi_display_get_rotate; |
| 12553 | + dssdev->set_rotate = dsi_display_set_rotate; |
| 12554 | + |
| 12555 | + dssdev->get_mirror = dsi_display_get_mirror; |
| 12556 | + dssdev->set_mirror = dsi_display_set_mirror; |
| 12557 | + |
| 12558 | + dssdev->run_test = dsi_display_run_test; |
| 12559 | + dssdev->memory_read = dsi_display_memory_read; |
| 12560 | + |
| 12561 | + dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; |
| 12562 | + |
| 12563 | + dsi.vc[0].dssdev = dssdev; |
| 12564 | + dsi.vc[1].dssdev = dssdev; |
| 12565 | + |
| 12566 | + return 0; |
| 12567 | +} |
| 12568 | + |
| 12569 | +int dsi_init(struct platform_device *pdev) |
| 12570 | +{ |
| 12571 | + u32 rev; |
| 12572 | + struct sched_param param = { |
| 12573 | + .sched_priority = MAX_USER_RT_PRIO-1 |
| 12574 | + }; |
| 12575 | + |
| 12576 | + spin_lock_init(&dsi.errors_lock); |
| 12577 | + dsi.errors = 0; |
| 12578 | + |
| 12579 | + /* XXX fail properly */ |
| 12580 | + |
| 12581 | + init_completion(&dsi.bta_completion); |
| 12582 | + init_completion(&dsi.update_completion); |
| 12583 | + |
| 12584 | + dsi.thread = kthread_create(dsi_update_thread, NULL, "dsi"); |
| 12585 | + if (IS_ERR(dsi.thread)) { |
| 12586 | + DSSERR("cannot create kthread\n"); |
| 12587 | + return PTR_ERR(dsi.thread); |
| 12588 | + } |
| 12589 | + sched_setscheduler(dsi.thread, SCHED_FIFO, ¶m); |
| 12590 | + |
| 12591 | + init_waitqueue_head(&dsi.waitqueue); |
| 12592 | + spin_lock_init(&dsi.update_lock); |
| 12593 | + |
| 12594 | + mutex_init(&dsi.lock); |
| 12595 | + mutex_init(&dsi.bus_lock); |
| 12596 | + |
| 12597 | + dsi.update_mode = OMAP_DSS_UPDATE_DISABLED; |
| 12598 | + dsi.user_update_mode = OMAP_DSS_UPDATE_DISABLED; |
| 12599 | + |
| 12600 | + dsi.base = ioremap(DSI_BASE, DSI_SZ_REGS); |
| 12601 | + if (!dsi.base) { |
| 12602 | + DSSERR("can't ioremap DSI\n"); |
| 12603 | + return -ENOMEM; |
| 12604 | + } |
| 12605 | + |
| 12606 | + dsi.vdds_dsi_reg = regulator_get(&pdev->dev, "vdds_dsi"); |
| 12607 | + if (IS_ERR(dsi.vdds_dsi_reg)) { |
| 12608 | + iounmap(dsi.base); |
| 12609 | + DSSERR("can't get VDDS_DSI regulator\n"); |
| 12610 | + return PTR_ERR(dsi.vdds_dsi_reg); |
| 12611 | + } |
| 12612 | + |
| 12613 | + enable_clocks(1); |
| 12614 | + |
| 12615 | + rev = dsi_read_reg(DSI_REVISION); |
| 12616 | + printk(KERN_INFO "OMAP DSI rev %d.%d\n", |
| 12617 | + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); |
| 12618 | + |
| 12619 | + enable_clocks(0); |
| 12620 | + |
| 12621 | + wake_up_process(dsi.thread); |
| 12622 | + |
| 12623 | + return 0; |
| 12624 | +} |
| 12625 | + |
| 12626 | +void dsi_exit(void) |
| 12627 | +{ |
| 12628 | + kthread_stop(dsi.thread); |
| 12629 | + |
| 12630 | + regulator_put(dsi.vdds_dsi_reg); |
| 12631 | + |
| 12632 | + iounmap(dsi.base); |
| 12633 | + |
| 12634 | + DSSDBG("omap_dsi_exit\n"); |
| 12635 | +} |
| 12636 | + |
| 12637 | --- /dev/null |
| 12638 | +++ b/drivers/video/omap2/dss/dss.c |
| 12639 | @@ -0,0 +1,347 @@ |
| 12640 | +/* |
| 12641 | + * linux/drivers/video/omap2/dss/dss.c |
| 12642 | + * |
| 12643 | + * Copyright (C) 2009 Nokia Corporation |
| 12644 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 12645 | + * |
| 12646 | + * Some code and ideas taken from drivers/video/omap/ driver |
| 12647 | + * by Imre Deak. |
| 12648 | + * |
| 12649 | + * This program is free software; you can redistribute it and/or modify it |
| 12650 | + * under the terms of the GNU General Public License version 2 as published by |
| 12651 | + * the Free Software Foundation. |
| 12652 | + * |
| 12653 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 12654 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 12655 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 12656 | + * more details. |
| 12657 | + * |
| 12658 | + * You should have received a copy of the GNU General Public License along with |
| 12659 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 12660 | + */ |
| 12661 | + |
| 12662 | +#define DSS_SUBSYS_NAME "DSS" |
| 12663 | + |
| 12664 | +#include <linux/kernel.h> |
| 12665 | +#include <linux/io.h> |
| 12666 | +#include <linux/err.h> |
| 12667 | +#include <linux/delay.h> |
| 12668 | +#include <linux/interrupt.h> |
| 12669 | +#include <linux/seq_file.h> |
| 12670 | + |
| 12671 | +#include <mach/display.h> |
| 12672 | +#include "dss.h" |
| 12673 | + |
| 12674 | +#define DSS_BASE 0x48050000 |
| 12675 | + |
| 12676 | +#define DSS_SZ_REGS SZ_512 |
| 12677 | + |
| 12678 | +struct dss_reg { |
| 12679 | + u16 idx; |
| 12680 | +}; |
| 12681 | + |
| 12682 | +#define DSS_REG(idx) ((const struct dss_reg) { idx }) |
| 12683 | + |
| 12684 | +#define DSS_REVISION DSS_REG(0x0000) |
| 12685 | +#define DSS_SYSCONFIG DSS_REG(0x0010) |
| 12686 | +#define DSS_SYSSTATUS DSS_REG(0x0014) |
| 12687 | +#define DSS_IRQSTATUS DSS_REG(0x0018) |
| 12688 | +#define DSS_CONTROL DSS_REG(0x0040) |
| 12689 | +#define DSS_SDI_CONTROL DSS_REG(0x0044) |
| 12690 | +#define DSS_PLL_CONTROL DSS_REG(0x0048) |
| 12691 | +#define DSS_SDI_STATUS DSS_REG(0x005C) |
| 12692 | + |
| 12693 | +#define REG_GET(idx, start, end) \ |
| 12694 | + FLD_GET(dss_read_reg(idx), start, end) |
| 12695 | + |
| 12696 | +#define REG_FLD_MOD(idx, val, start, end) \ |
| 12697 | + dss_write_reg(idx, FLD_MOD(dss_read_reg(idx), val, start, end)) |
| 12698 | + |
| 12699 | +static struct { |
| 12700 | + void __iomem *base; |
| 12701 | + |
| 12702 | + u32 ctx[DSS_SZ_REGS / sizeof(u32)]; |
| 12703 | +} dss; |
| 12704 | + |
| 12705 | +static int _omap_dss_wait_reset(void); |
| 12706 | + |
| 12707 | +static inline void dss_write_reg(const struct dss_reg idx, u32 val) |
| 12708 | +{ |
| 12709 | + __raw_writel(val, dss.base + idx.idx); |
| 12710 | +} |
| 12711 | + |
| 12712 | +static inline u32 dss_read_reg(const struct dss_reg idx) |
| 12713 | +{ |
| 12714 | + return __raw_readl(dss.base + idx.idx); |
| 12715 | +} |
| 12716 | + |
| 12717 | +#define SR(reg) \ |
| 12718 | + dss.ctx[(DSS_##reg).idx / sizeof(u32)] = dss_read_reg(DSS_##reg) |
| 12719 | +#define RR(reg) \ |
| 12720 | + dss_write_reg(DSS_##reg, dss.ctx[(DSS_##reg).idx / sizeof(u32)]) |
| 12721 | + |
| 12722 | +void dss_save_context(void) |
| 12723 | +{ |
| 12724 | + if (cpu_is_omap24xx()) |
| 12725 | + return; |
| 12726 | + |
| 12727 | + SR(SYSCONFIG); |
| 12728 | + SR(CONTROL); |
| 12729 | + |
| 12730 | +#ifdef CONFIG_OMAP2_DSS_SDI |
| 12731 | + SR(SDI_CONTROL); |
| 12732 | + SR(PLL_CONTROL); |
| 12733 | +#endif |
| 12734 | +} |
| 12735 | + |
| 12736 | +void dss_restore_context(void) |
| 12737 | +{ |
| 12738 | + if (_omap_dss_wait_reset()) |
| 12739 | + DSSERR("DSS not coming out of reset after sleep\n"); |
| 12740 | + |
| 12741 | + RR(SYSCONFIG); |
| 12742 | + RR(CONTROL); |
| 12743 | + |
| 12744 | +#ifdef CONFIG_OMAP2_DSS_SDI |
| 12745 | + RR(SDI_CONTROL); |
| 12746 | + RR(PLL_CONTROL); |
| 12747 | +#endif |
| 12748 | +} |
| 12749 | + |
| 12750 | +#undef SR |
| 12751 | +#undef RR |
| 12752 | + |
| 12753 | +void dss_sdi_init(u8 datapairs) |
| 12754 | +{ |
| 12755 | + u32 l; |
| 12756 | + |
| 12757 | + BUG_ON(datapairs > 3 || datapairs < 1); |
| 12758 | + |
| 12759 | + l = dss_read_reg(DSS_SDI_CONTROL); |
| 12760 | + l = FLD_MOD(l, 0xf, 19, 15); /* SDI_PDIV */ |
| 12761 | + l = FLD_MOD(l, datapairs-1, 3, 2); /* SDI_PRSEL */ |
| 12762 | + l = FLD_MOD(l, 2, 1, 0); /* SDI_BWSEL */ |
| 12763 | + dss_write_reg(DSS_SDI_CONTROL, l); |
| 12764 | + |
| 12765 | + l = dss_read_reg(DSS_PLL_CONTROL); |
| 12766 | + l = FLD_MOD(l, 0x7, 25, 22); /* SDI_PLL_FREQSEL */ |
| 12767 | + l = FLD_MOD(l, 0xb, 16, 11); /* SDI_PLL_REGN */ |
| 12768 | + l = FLD_MOD(l, 0xb4, 10, 1); /* SDI_PLL_REGM */ |
| 12769 | + dss_write_reg(DSS_PLL_CONTROL, l); |
| 12770 | +} |
| 12771 | + |
| 12772 | +void dss_sdi_enable(void) |
| 12773 | +{ |
| 12774 | + dispc_pck_free_enable(1); |
| 12775 | + |
| 12776 | + /* Reset SDI PLL */ |
| 12777 | + REG_FLD_MOD(DSS_PLL_CONTROL, 1, 18, 18); /* SDI_PLL_SYSRESET */ |
| 12778 | + udelay(1); /* wait 2x PCLK */ |
| 12779 | + |
| 12780 | + /* Lock SDI PLL */ |
| 12781 | + REG_FLD_MOD(DSS_PLL_CONTROL, 1, 28, 28); /* SDI_PLL_GOBIT */ |
| 12782 | + |
| 12783 | + /* Waiting for PLL lock request to complete */ |
| 12784 | + while (dss_read_reg(DSS_SDI_STATUS) & (1 << 6)) |
| 12785 | + ; |
| 12786 | + |
| 12787 | + /* Clearing PLL_GO bit */ |
| 12788 | + REG_FLD_MOD(DSS_PLL_CONTROL, 0, 28, 28); |
| 12789 | + |
| 12790 | + /* Waiting for PLL to lock */ |
| 12791 | + while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 5))) |
| 12792 | + ; |
| 12793 | + |
| 12794 | + dispc_lcd_enable_signal(1); |
| 12795 | + |
| 12796 | + /* Waiting for SDI reset to complete */ |
| 12797 | + while (!(dss_read_reg(DSS_SDI_STATUS) & (1 << 2))) |
| 12798 | + ; |
| 12799 | +} |
| 12800 | + |
| 12801 | +void dss_sdi_disable(void) |
| 12802 | +{ |
| 12803 | + dispc_lcd_enable_signal(0); |
| 12804 | + |
| 12805 | + dispc_pck_free_enable(0); |
| 12806 | + |
| 12807 | + /* Reset SDI PLL */ |
| 12808 | + REG_FLD_MOD(DSS_PLL_CONTROL, 0, 18, 18); /* SDI_PLL_SYSRESET */ |
| 12809 | +} |
| 12810 | + |
| 12811 | +void dss_dump_regs(struct seq_file *s) |
| 12812 | +{ |
| 12813 | +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, dss_read_reg(r)) |
| 12814 | + |
| 12815 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 12816 | + |
| 12817 | + DUMPREG(DSS_REVISION); |
| 12818 | + DUMPREG(DSS_SYSCONFIG); |
| 12819 | + DUMPREG(DSS_SYSSTATUS); |
| 12820 | + DUMPREG(DSS_IRQSTATUS); |
| 12821 | + DUMPREG(DSS_CONTROL); |
| 12822 | + DUMPREG(DSS_SDI_CONTROL); |
| 12823 | + DUMPREG(DSS_PLL_CONTROL); |
| 12824 | + DUMPREG(DSS_SDI_STATUS); |
| 12825 | + |
| 12826 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 12827 | +#undef DUMPREG |
| 12828 | +} |
| 12829 | + |
| 12830 | +void dss_select_clk_source(bool dsi, bool dispc) |
| 12831 | +{ |
| 12832 | + u32 r; |
| 12833 | + r = dss_read_reg(DSS_CONTROL); |
| 12834 | + r = FLD_MOD(r, dsi, 1, 1); /* DSI_CLK_SWITCH */ |
| 12835 | + r = FLD_MOD(r, dispc, 0, 0); /* DISPC_CLK_SWITCH */ |
| 12836 | + dss_write_reg(DSS_CONTROL, r); |
| 12837 | +} |
| 12838 | + |
| 12839 | +int dss_get_dsi_clk_source(void) |
| 12840 | +{ |
| 12841 | + return FLD_GET(dss_read_reg(DSS_CONTROL), 1, 1); |
| 12842 | +} |
| 12843 | + |
| 12844 | +int dss_get_dispc_clk_source(void) |
| 12845 | +{ |
| 12846 | + return FLD_GET(dss_read_reg(DSS_CONTROL), 0, 0); |
| 12847 | +} |
| 12848 | + |
| 12849 | +static irqreturn_t dss_irq_handler_omap2(int irq, void *arg) |
| 12850 | +{ |
| 12851 | + dispc_irq_handler(); |
| 12852 | + |
| 12853 | + return IRQ_HANDLED; |
| 12854 | +} |
| 12855 | + |
| 12856 | +static irqreturn_t dss_irq_handler_omap3(int irq, void *arg) |
| 12857 | +{ |
| 12858 | + u32 irqstatus; |
| 12859 | + |
| 12860 | + irqstatus = dss_read_reg(DSS_IRQSTATUS); |
| 12861 | + |
| 12862 | + if (irqstatus & (1<<0)) /* DISPC_IRQ */ |
| 12863 | + dispc_irq_handler(); |
| 12864 | +#ifdef CONFIG_OMAP2_DSS_DSI |
| 12865 | + if (irqstatus & (1<<1)) /* DSI_IRQ */ |
| 12866 | + dsi_irq_handler(); |
| 12867 | +#endif |
| 12868 | + |
| 12869 | + return IRQ_HANDLED; |
| 12870 | +} |
| 12871 | + |
| 12872 | +static int _omap_dss_wait_reset(void) |
| 12873 | +{ |
| 12874 | + unsigned timeout = 1000; |
| 12875 | + |
| 12876 | + while (REG_GET(DSS_SYSSTATUS, 0, 0) == 0) { |
| 12877 | + udelay(1); |
| 12878 | + if (!--timeout) { |
| 12879 | + DSSERR("soft reset failed\n"); |
| 12880 | + return -ENODEV; |
| 12881 | + } |
| 12882 | + } |
| 12883 | + |
| 12884 | + return 0; |
| 12885 | +} |
| 12886 | + |
| 12887 | +static int _omap_dss_reset(void) |
| 12888 | +{ |
| 12889 | + /* Soft reset */ |
| 12890 | + REG_FLD_MOD(DSS_SYSCONFIG, 1, 1, 1); |
| 12891 | + return _omap_dss_wait_reset(); |
| 12892 | +} |
| 12893 | + |
| 12894 | +void dss_set_venc_output(enum omap_dss_venc_type type) |
| 12895 | +{ |
| 12896 | + int l = 0; |
| 12897 | + |
| 12898 | + if (type == OMAP_DSS_VENC_TYPE_COMPOSITE) |
| 12899 | + l = 0; |
| 12900 | + else if (type == OMAP_DSS_VENC_TYPE_SVIDEO) |
| 12901 | + l = 1; |
| 12902 | + else |
| 12903 | + BUG(); |
| 12904 | + |
| 12905 | + /* venc out selection. 0 = comp, 1 = svideo */ |
| 12906 | + REG_FLD_MOD(DSS_CONTROL, l, 6, 6); |
| 12907 | +} |
| 12908 | + |
| 12909 | +void dss_set_dac_pwrdn_bgz(bool enable) |
| 12910 | +{ |
| 12911 | + REG_FLD_MOD(DSS_CONTROL, enable, 5, 5); /* DAC Power-Down Control */ |
| 12912 | +} |
| 12913 | + |
| 12914 | +int dss_init(bool skip_init) |
| 12915 | +{ |
| 12916 | + int r; |
| 12917 | + u32 rev; |
| 12918 | + |
| 12919 | + dss.base = ioremap(DSS_BASE, DSS_SZ_REGS); |
| 12920 | + if (!dss.base) { |
| 12921 | + DSSERR("can't ioremap DSS\n"); |
| 12922 | + r = -ENOMEM; |
| 12923 | + goto fail0; |
| 12924 | + } |
| 12925 | + |
| 12926 | + if (!skip_init) { |
| 12927 | + /* disable LCD and DIGIT output. This seems to fix the synclost |
| 12928 | + * problem that we get, if the bootloader starts the DSS and |
| 12929 | + * the kernel resets it */ |
| 12930 | + omap_writel(omap_readl(0x48050440) & ~0x3, 0x48050440); |
| 12931 | + |
| 12932 | + /* We need to wait here a bit, otherwise we sometimes start to |
| 12933 | + * get synclost errors, and after that only power cycle will |
| 12934 | + * restore DSS functionality. I have no idea why this happens. |
| 12935 | + * And we have to wait _before_ resetting the DSS, but after |
| 12936 | + * enabling clocks. |
| 12937 | + */ |
| 12938 | + msleep(50); |
| 12939 | + |
| 12940 | + _omap_dss_reset(); |
| 12941 | + } |
| 12942 | + |
| 12943 | + /* autoidle */ |
| 12944 | + REG_FLD_MOD(DSS_SYSCONFIG, 1, 0, 0); |
| 12945 | + |
| 12946 | + /* Select DPLL */ |
| 12947 | + REG_FLD_MOD(DSS_CONTROL, 0, 0, 0); |
| 12948 | + |
| 12949 | +#ifdef CONFIG_OMAP2_DSS_VENC |
| 12950 | + REG_FLD_MOD(DSS_CONTROL, 1, 4, 4); /* venc dac demen */ |
| 12951 | + REG_FLD_MOD(DSS_CONTROL, 1, 3, 3); /* venc clock 4x enable */ |
| 12952 | + REG_FLD_MOD(DSS_CONTROL, 0, 2, 2); /* venc clock mode = normal */ |
| 12953 | +#endif |
| 12954 | + |
| 12955 | + r = request_irq(INT_24XX_DSS_IRQ, |
| 12956 | + cpu_is_omap24xx() |
| 12957 | + ? dss_irq_handler_omap2 |
| 12958 | + : dss_irq_handler_omap3, |
| 12959 | + 0, "OMAP DSS", NULL); |
| 12960 | + |
| 12961 | + if (r < 0) { |
| 12962 | + DSSERR("omap2 dss: request_irq failed\n"); |
| 12963 | + goto fail1; |
| 12964 | + } |
| 12965 | + |
| 12966 | + dss_save_context(); |
| 12967 | + |
| 12968 | + rev = dss_read_reg(DSS_REVISION); |
| 12969 | + printk(KERN_INFO "OMAP DSS rev %d.%d\n", |
| 12970 | + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); |
| 12971 | + |
| 12972 | + return 0; |
| 12973 | + |
| 12974 | +fail1: |
| 12975 | + iounmap(dss.base); |
| 12976 | +fail0: |
| 12977 | + return r; |
| 12978 | +} |
| 12979 | + |
| 12980 | +void dss_exit(void) |
| 12981 | +{ |
| 12982 | + free_irq(INT_24XX_DSS_IRQ, NULL); |
| 12983 | + |
| 12984 | + iounmap(dss.base); |
| 12985 | +} |
| 12986 | + |
| 12987 | --- /dev/null |
| 12988 | +++ b/drivers/video/omap2/dss/dss.h |
| 12989 | @@ -0,0 +1,356 @@ |
| 12990 | +/* |
| 12991 | + * linux/drivers/video/omap2/dss/dss.h |
| 12992 | + * |
| 12993 | + * Copyright (C) 2009 Nokia Corporation |
| 12994 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 12995 | + * |
| 12996 | + * Some code and ideas taken from drivers/video/omap/ driver |
| 12997 | + * by Imre Deak. |
| 12998 | + * |
| 12999 | + * This program is free software; you can redistribute it and/or modify it |
| 13000 | + * under the terms of the GNU General Public License version 2 as published by |
| 13001 | + * the Free Software Foundation. |
| 13002 | + * |
| 13003 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 13004 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 13005 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 13006 | + * more details. |
| 13007 | + * |
| 13008 | + * You should have received a copy of the GNU General Public License along with |
| 13009 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 13010 | + */ |
| 13011 | + |
| 13012 | +#ifndef __OMAP2_DSS_H |
| 13013 | +#define __OMAP2_DSS_H |
| 13014 | + |
| 13015 | +#ifdef CONFIG_OMAP2_DSS_DEBUG_SUPPORT |
| 13016 | +#define DEBUG |
| 13017 | +#endif |
| 13018 | + |
| 13019 | +#ifdef DEBUG |
| 13020 | +extern unsigned int dss_debug; |
| 13021 | +#ifdef DSS_SUBSYS_NAME |
| 13022 | +#define DSSDBG(format, ...) \ |
| 13023 | + if (dss_debug) \ |
| 13024 | + printk(KERN_DEBUG "omapdss " DSS_SUBSYS_NAME ": " format, \ |
| 13025 | + ## __VA_ARGS__) |
| 13026 | +#else |
| 13027 | +#define DSSDBG(format, ...) \ |
| 13028 | + if (dss_debug) \ |
| 13029 | + printk(KERN_DEBUG "omapdss: " format, ## __VA_ARGS__) |
| 13030 | +#endif |
| 13031 | + |
| 13032 | +#ifdef DSS_SUBSYS_NAME |
| 13033 | +#define DSSDBGF(format, ...) \ |
| 13034 | + if (dss_debug) \ |
| 13035 | + printk(KERN_DEBUG "omapdss " DSS_SUBSYS_NAME \ |
| 13036 | + ": %s(" format ")\n", \ |
| 13037 | + __func__, \ |
| 13038 | + ## __VA_ARGS__) |
| 13039 | +#else |
| 13040 | +#define DSSDBGF(format, ...) \ |
| 13041 | + if (dss_debug) \ |
| 13042 | + printk(KERN_DEBUG "omapdss: " \ |
| 13043 | + ": %s(" format ")\n", \ |
| 13044 | + __func__, \ |
| 13045 | + ## __VA_ARGS__) |
| 13046 | +#endif |
| 13047 | + |
| 13048 | +#else /* DEBUG */ |
| 13049 | +#define DSSDBG(format, ...) |
| 13050 | +#define DSSDBGF(format, ...) |
| 13051 | +#endif |
| 13052 | + |
| 13053 | + |
| 13054 | +#ifdef DSS_SUBSYS_NAME |
| 13055 | +#define DSSERR(format, ...) \ |
| 13056 | + printk(KERN_ERR "omapdss " DSS_SUBSYS_NAME " error: " format, \ |
| 13057 | + ## __VA_ARGS__) |
| 13058 | +#else |
| 13059 | +#define DSSERR(format, ...) \ |
| 13060 | + printk(KERN_ERR "omapdss error: " format, ## __VA_ARGS__) |
| 13061 | +#endif |
| 13062 | + |
| 13063 | +#ifdef DSS_SUBSYS_NAME |
| 13064 | +#define DSSINFO(format, ...) \ |
| 13065 | + printk(KERN_INFO "omapdss " DSS_SUBSYS_NAME ": " format, \ |
| 13066 | + ## __VA_ARGS__) |
| 13067 | +#else |
| 13068 | +#define DSSINFO(format, ...) \ |
| 13069 | + printk(KERN_INFO "omapdss: " format, ## __VA_ARGS__) |
| 13070 | +#endif |
| 13071 | + |
| 13072 | +#ifdef DSS_SUBSYS_NAME |
| 13073 | +#define DSSWARN(format, ...) \ |
| 13074 | + printk(KERN_WARNING "omapdss " DSS_SUBSYS_NAME ": " format, \ |
| 13075 | + ## __VA_ARGS__) |
| 13076 | +#else |
| 13077 | +#define DSSWARN(format, ...) \ |
| 13078 | + printk(KERN_WARNING "omapdss: " format, ## __VA_ARGS__) |
| 13079 | +#endif |
| 13080 | + |
| 13081 | +/* OMAP TRM gives bitfields as start:end, where start is the higher bit |
| 13082 | + number. For example 7:0 */ |
| 13083 | +#define FLD_MASK(start, end) (((1 << (start - end + 1)) - 1) << (end)) |
| 13084 | +#define FLD_VAL(val, start, end) (((val) << end) & FLD_MASK(start, end)) |
| 13085 | +#define FLD_GET(val, start, end) (((val) & FLD_MASK(start, end)) >> (end)) |
| 13086 | +#define FLD_MOD(orig, val, start, end) \ |
| 13087 | + (((orig) & ~FLD_MASK(start, end)) | FLD_VAL(val, start, end)) |
| 13088 | + |
| 13089 | +#define DISPC_MAX_FCK 173000000 |
| 13090 | + |
| 13091 | +enum omap_burst_size { |
| 13092 | + OMAP_DSS_BURST_4x32 = 0, |
| 13093 | + OMAP_DSS_BURST_8x32 = 1, |
| 13094 | + OMAP_DSS_BURST_16x32 = 2, |
| 13095 | +}; |
| 13096 | + |
| 13097 | +enum omap_parallel_interface_mode { |
| 13098 | + OMAP_DSS_PARALLELMODE_BYPASS, /* MIPI DPI */ |
| 13099 | + OMAP_DSS_PARALLELMODE_RFBI, /* MIPI DBI */ |
| 13100 | + OMAP_DSS_PARALLELMODE_DSI, |
| 13101 | +}; |
| 13102 | + |
| 13103 | +enum dss_clock { |
| 13104 | + DSS_CLK_ICK = 1 << 0, |
| 13105 | + DSS_CLK_FCK1 = 1 << 1, |
| 13106 | + DSS_CLK_FCK2 = 1 << 2, |
| 13107 | + DSS_CLK_54M = 1 << 3, |
| 13108 | + DSS_CLK_96M = 1 << 4, |
| 13109 | +}; |
| 13110 | + |
| 13111 | +struct dispc_clock_info { |
| 13112 | + /* rates that we get with dividers below */ |
| 13113 | + unsigned long fck; |
| 13114 | + unsigned long lck; |
| 13115 | + unsigned long pck; |
| 13116 | + |
| 13117 | + /* dividers */ |
| 13118 | + u16 fck_div; |
| 13119 | + u16 lck_div; |
| 13120 | + u16 pck_div; |
| 13121 | +}; |
| 13122 | + |
| 13123 | +struct dsi_clock_info { |
| 13124 | + /* rates that we get with dividers below */ |
| 13125 | + unsigned long fint; |
| 13126 | + unsigned long dsiphy; |
| 13127 | + unsigned long clkin; |
| 13128 | + unsigned long dsi1_pll_fclk; |
| 13129 | + unsigned long dsi2_pll_fclk; |
| 13130 | + unsigned long lck; |
| 13131 | + unsigned long pck; |
| 13132 | + |
| 13133 | + /* dividers */ |
| 13134 | + u16 regn; |
| 13135 | + u16 regm; |
| 13136 | + u16 regm3; |
| 13137 | + u16 regm4; |
| 13138 | + |
| 13139 | + u16 lck_div; |
| 13140 | + u16 pck_div; |
| 13141 | + |
| 13142 | + u8 highfreq; |
| 13143 | + bool use_dss2_fck; |
| 13144 | +}; |
| 13145 | + |
| 13146 | +struct seq_file; |
| 13147 | +struct platform_device; |
| 13148 | + |
| 13149 | +/* core */ |
| 13150 | +void dss_clk_enable(enum dss_clock clks); |
| 13151 | +void dss_clk_disable(enum dss_clock clks); |
| 13152 | +unsigned long dss_clk_get_rate(enum dss_clock clk); |
| 13153 | +int dss_need_ctx_restore(void); |
| 13154 | +void dss_dump_clocks(struct seq_file *s); |
| 13155 | +struct bus_type *dss_get_bus(void); |
| 13156 | + |
| 13157 | +/* display */ |
| 13158 | +int dss_suspend_all_devices(void); |
| 13159 | +int dss_resume_all_devices(void); |
| 13160 | + |
| 13161 | +void dss_init_device(struct platform_device *pdev, |
| 13162 | + struct omap_dss_device *dssdev); |
| 13163 | +void dss_uninit_device(struct platform_device *pdev, |
| 13164 | + struct omap_dss_device *dssdev); |
| 13165 | +bool dss_use_replication(struct omap_dss_device *dssdev, |
| 13166 | + enum omap_color_mode mode); |
| 13167 | +void default_get_overlay_fifo_thresholds(enum omap_plane plane, |
| 13168 | + u32 fifo_size, enum omap_burst_size *burst_size, |
| 13169 | + u32 *fifo_low, u32 *fifo_high); |
| 13170 | + |
| 13171 | +/* manager */ |
| 13172 | +int dss_init_overlay_managers(struct platform_device *pdev); |
| 13173 | +void dss_uninit_overlay_managers(struct platform_device *pdev); |
| 13174 | +int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl); |
| 13175 | +void dss_setup_partial_planes(struct omap_dss_device *dssdev, |
| 13176 | + u16 *x, u16 *y, u16 *w, u16 *h); |
| 13177 | +void dss_start_update(struct omap_dss_device *dssdev); |
| 13178 | + |
| 13179 | +/* overlay */ |
| 13180 | +void dss_init_overlays(struct platform_device *pdev); |
| 13181 | +void dss_uninit_overlays(struct platform_device *pdev); |
| 13182 | +int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev); |
| 13183 | +void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr); |
| 13184 | +#ifdef L4_EXAMPLE |
| 13185 | +void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr); |
| 13186 | +#endif |
| 13187 | +void dss_recheck_connections(struct omap_dss_device *dssdev, bool force); |
| 13188 | + |
| 13189 | +/* DSS */ |
| 13190 | +int dss_init(bool skip_init); |
| 13191 | +void dss_exit(void); |
| 13192 | + |
| 13193 | +void dss_save_context(void); |
| 13194 | +void dss_restore_context(void); |
| 13195 | + |
| 13196 | +void dss_dump_regs(struct seq_file *s); |
| 13197 | + |
| 13198 | +void dss_sdi_init(u8 datapairs); |
| 13199 | +void dss_sdi_enable(void); |
| 13200 | +void dss_sdi_disable(void); |
| 13201 | + |
| 13202 | +void dss_select_clk_source(bool dsi, bool dispc); |
| 13203 | +int dss_get_dsi_clk_source(void); |
| 13204 | +int dss_get_dispc_clk_source(void); |
| 13205 | +void dss_set_venc_output(enum omap_dss_venc_type type); |
| 13206 | +void dss_set_dac_pwrdn_bgz(bool enable); |
| 13207 | + |
| 13208 | +/* SDI */ |
| 13209 | +int sdi_init(bool skip_init); |
| 13210 | +void sdi_exit(void); |
| 13211 | +int sdi_init_display(struct omap_dss_device *display); |
| 13212 | + |
| 13213 | +/* DSI */ |
| 13214 | +int dsi_init(struct platform_device *pdev); |
| 13215 | +void dsi_exit(void); |
| 13216 | + |
| 13217 | +void dsi_dump_clocks(struct seq_file *s); |
| 13218 | +void dsi_dump_regs(struct seq_file *s); |
| 13219 | + |
| 13220 | +void dsi_save_context(void); |
| 13221 | +void dsi_restore_context(void); |
| 13222 | + |
| 13223 | +int dsi_init_display(struct omap_dss_device *display); |
| 13224 | +void dsi_irq_handler(void); |
| 13225 | +unsigned long dsi_get_dsi1_pll_rate(void); |
| 13226 | +unsigned long dsi_get_dsi2_pll_rate(void); |
| 13227 | +int dsi_pll_calc_pck(bool is_tft, unsigned long req_pck, |
| 13228 | + struct dsi_clock_info *cinfo); |
| 13229 | +int dsi_pll_program(struct dsi_clock_info *cinfo); |
| 13230 | +int dsi_pll_init(bool enable_hsclk, bool enable_hsdiv); |
| 13231 | +void dsi_pll_uninit(void); |
| 13232 | +void dsi_get_overlay_fifo_thresholds(enum omap_plane plane, |
| 13233 | + u32 fifo_size, enum omap_burst_size *burst_size, |
| 13234 | + u32 *fifo_low, u32 *fifo_high); |
| 13235 | + |
| 13236 | +/* DPI */ |
| 13237 | +int dpi_init(void); |
| 13238 | +void dpi_exit(void); |
| 13239 | +int dpi_init_display(struct omap_dss_device *dssdev); |
| 13240 | + |
| 13241 | +/* DISPC */ |
| 13242 | +int dispc_init(void); |
| 13243 | +void dispc_exit(void); |
| 13244 | +void dispc_dump_clocks(struct seq_file *s); |
| 13245 | +void dispc_dump_regs(struct seq_file *s); |
| 13246 | +void dispc_irq_handler(void); |
| 13247 | +void dispc_fake_vsync_irq(void); |
| 13248 | + |
| 13249 | +void dispc_save_context(void); |
| 13250 | +void dispc_restore_context(void); |
| 13251 | + |
| 13252 | +void dispc_enable_sidle(void); |
| 13253 | +void dispc_disable_sidle(void); |
| 13254 | + |
| 13255 | +void dispc_lcd_enable_signal_polarity(bool act_high); |
| 13256 | +void dispc_lcd_enable_signal(bool enable); |
| 13257 | +void dispc_pck_free_enable(bool enable); |
| 13258 | +void dispc_enable_fifohandcheck(bool enable); |
| 13259 | + |
| 13260 | +void dispc_set_lcd_size(u16 width, u16 height); |
| 13261 | +void dispc_set_digit_size(u16 width, u16 height); |
| 13262 | +u32 dispc_get_plane_fifo_size(enum omap_plane plane); |
| 13263 | +void dispc_setup_plane_fifo(enum omap_plane plane, u32 low, u32 high); |
| 13264 | +void dispc_enable_fifomerge(bool enable); |
| 13265 | +void dispc_set_burst_size(enum omap_plane plane, |
| 13266 | + enum omap_burst_size burst_size); |
| 13267 | + |
| 13268 | +void dispc_set_plane_ba0(enum omap_plane plane, u32 paddr); |
| 13269 | +void dispc_set_plane_ba1(enum omap_plane plane, u32 paddr); |
| 13270 | +void dispc_set_plane_pos(enum omap_plane plane, u16 x, u16 y); |
| 13271 | +void dispc_set_plane_size(enum omap_plane plane, u16 width, u16 height); |
| 13272 | +void dispc_set_channel_out(enum omap_plane plane, |
| 13273 | + enum omap_channel channel_out); |
| 13274 | + |
| 13275 | +int dispc_setup_plane(enum omap_plane plane, |
| 13276 | + u32 paddr, u16 screen_width, |
| 13277 | + u16 pos_x, u16 pos_y, |
| 13278 | + u16 width, u16 height, |
| 13279 | + u16 out_width, u16 out_height, |
| 13280 | + enum omap_color_mode color_mode, |
| 13281 | + bool ilace, |
| 13282 | + enum omap_dss_rotation_type rotation_type, |
| 13283 | + u8 rotation, bool mirror, |
| 13284 | + u8 global_alpha); |
| 13285 | + |
| 13286 | +bool dispc_go_busy(enum omap_channel channel); |
| 13287 | +void dispc_go(enum omap_channel channel); |
| 13288 | +void dispc_enable_lcd_out(bool enable); |
| 13289 | +void dispc_enable_digit_out(bool enable); |
| 13290 | +int dispc_enable_plane(enum omap_plane plane, bool enable); |
| 13291 | +void dispc_enable_replication(enum omap_plane plane, bool enable); |
| 13292 | + |
| 13293 | +void dispc_set_parallel_interface_mode(enum omap_parallel_interface_mode mode); |
| 13294 | +void dispc_set_tft_data_lines(u8 data_lines); |
| 13295 | +void dispc_set_lcd_display_type(enum omap_lcd_display_type type); |
| 13296 | +void dispc_set_loadmode(enum omap_dss_load_mode mode); |
| 13297 | + |
| 13298 | +void dispc_set_default_color(enum omap_channel channel, u32 color); |
| 13299 | +u32 dispc_get_default_color(enum omap_channel channel); |
| 13300 | +void dispc_set_trans_key(enum omap_channel ch, |
| 13301 | + enum omap_dss_trans_key_type type, |
| 13302 | + u32 trans_key); |
| 13303 | +void dispc_get_trans_key(enum omap_channel ch, |
| 13304 | + enum omap_dss_trans_key_type *type, |
| 13305 | + u32 *trans_key); |
| 13306 | +void dispc_enable_trans_key(enum omap_channel ch, bool enable); |
| 13307 | +void dispc_enable_alpha_blending(enum omap_channel ch, bool enable); |
| 13308 | +bool dispc_trans_key_enabled(enum omap_channel ch); |
| 13309 | +bool dispc_alpha_blending_enabled(enum omap_channel ch); |
| 13310 | + |
| 13311 | +bool dispc_lcd_timings_ok(struct omap_video_timings *timings); |
| 13312 | +void dispc_set_lcd_timings(struct omap_video_timings *timings); |
| 13313 | +unsigned long dispc_fclk_rate(void); |
| 13314 | +unsigned long dispc_lclk_rate(void); |
| 13315 | +unsigned long dispc_pclk_rate(void); |
| 13316 | +void dispc_set_pol_freq(enum omap_panel_config config, u8 acbi, u8 acb); |
| 13317 | +void find_lck_pck_divs(bool is_tft, unsigned long req_pck, unsigned long fck, |
| 13318 | + u16 *lck_div, u16 *pck_div); |
| 13319 | +int dispc_calc_clock_div(bool is_tft, unsigned long req_pck, |
| 13320 | + struct dispc_clock_info *cinfo); |
| 13321 | +int dispc_set_clock_div(struct dispc_clock_info *cinfo); |
| 13322 | +int dispc_get_clock_div(struct dispc_clock_info *cinfo); |
| 13323 | +void dispc_set_lcd_divisor(u16 lck_div, u16 pck_div); |
| 13324 | + |
| 13325 | + |
| 13326 | +/* VENC */ |
| 13327 | +int venc_init(struct platform_device *pdev); |
| 13328 | +void venc_exit(void); |
| 13329 | +void venc_dump_regs(struct seq_file *s); |
| 13330 | +int venc_init_display(struct omap_dss_device *display); |
| 13331 | + |
| 13332 | +/* RFBI */ |
| 13333 | +int rfbi_init(void); |
| 13334 | +void rfbi_exit(void); |
| 13335 | +void rfbi_dump_regs(struct seq_file *s); |
| 13336 | + |
| 13337 | +int rfbi_configure(int rfbi_module, int bpp, int lines); |
| 13338 | +void rfbi_enable_rfbi(bool enable); |
| 13339 | +void rfbi_transfer_area(u16 width, u16 height, |
| 13340 | + void (callback)(void *data), void *data); |
| 13341 | +void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t); |
| 13342 | +unsigned long rfbi_get_max_tx_rate(void); |
| 13343 | +int rfbi_init_display(struct omap_dss_device *display); |
| 13344 | + |
| 13345 | +#endif |
| 13346 | --- /dev/null |
| 13347 | +++ b/drivers/video/omap2/dss/manager.c |
| 13348 | @@ -0,0 +1,1487 @@ |
| 13349 | +/* |
| 13350 | + * linux/drivers/video/omap2/dss/manager.c |
| 13351 | + * |
| 13352 | + * Copyright (C) 2009 Nokia Corporation |
| 13353 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 13354 | + * |
| 13355 | + * Some code and ideas taken from drivers/video/omap/ driver |
| 13356 | + * by Imre Deak. |
| 13357 | + * |
| 13358 | + * This program is free software; you can redistribute it and/or modify it |
| 13359 | + * under the terms of the GNU General Public License version 2 as published by |
| 13360 | + * the Free Software Foundation. |
| 13361 | + * |
| 13362 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 13363 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 13364 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 13365 | + * more details. |
| 13366 | + * |
| 13367 | + * You should have received a copy of the GNU General Public License along with |
| 13368 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 13369 | + */ |
| 13370 | + |
| 13371 | +#define DSS_SUBSYS_NAME "MANAGER" |
| 13372 | + |
| 13373 | +#include <linux/kernel.h> |
| 13374 | +#include <linux/module.h> |
| 13375 | +#include <linux/platform_device.h> |
| 13376 | +#include <linux/spinlock.h> |
| 13377 | +#include <linux/jiffies.h> |
| 13378 | + |
| 13379 | +#include <mach/display.h> |
| 13380 | +#include <mach/cpu.h> |
| 13381 | + |
| 13382 | +#include "dss.h" |
| 13383 | + |
| 13384 | +static int num_managers; |
| 13385 | +static struct list_head manager_list; |
| 13386 | + |
| 13387 | +static ssize_t manager_name_show(struct omap_overlay_manager *mgr, char *buf) |
| 13388 | +{ |
| 13389 | + return snprintf(buf, PAGE_SIZE, "%s\n", mgr->name); |
| 13390 | +} |
| 13391 | + |
| 13392 | +static ssize_t manager_display_show(struct omap_overlay_manager *mgr, char *buf) |
| 13393 | +{ |
| 13394 | + return snprintf(buf, PAGE_SIZE, "%s\n", |
| 13395 | + mgr->device ? mgr->device->name : "<none>"); |
| 13396 | +} |
| 13397 | + |
| 13398 | +static ssize_t manager_display_store(struct omap_overlay_manager *mgr, |
| 13399 | + const char *buf, size_t size) |
| 13400 | +{ |
| 13401 | + int r = 0; |
| 13402 | + size_t len = size; |
| 13403 | + struct omap_dss_device *dssdev = NULL; |
| 13404 | + |
| 13405 | + int match(struct omap_dss_device *dssdev, void *data) |
| 13406 | + { |
| 13407 | + const char *str = data; |
| 13408 | + return sysfs_streq(dssdev->name, str); |
| 13409 | + } |
| 13410 | + |
| 13411 | + if (buf[size-1] == '\n') |
| 13412 | + --len; |
| 13413 | + |
| 13414 | + if (len > 0) |
| 13415 | + dssdev = omap_dss_find_device((void *)buf, match); |
| 13416 | + |
| 13417 | + if (len > 0 && dssdev == NULL) |
| 13418 | + return -EINVAL; |
| 13419 | + |
| 13420 | + if (dssdev) |
| 13421 | + DSSDBG("display %s found\n", dssdev->name); |
| 13422 | + |
| 13423 | + if (mgr->device) { |
| 13424 | + r = mgr->unset_device(mgr); |
| 13425 | + if (r) { |
| 13426 | + DSSERR("failed to unset display\n"); |
| 13427 | + goto put_device; |
| 13428 | + } |
| 13429 | + } |
| 13430 | + |
| 13431 | + if (dssdev) { |
| 13432 | + r = mgr->set_device(mgr, dssdev); |
| 13433 | + if (r) { |
| 13434 | + DSSERR("failed to set manager\n"); |
| 13435 | + goto put_device; |
| 13436 | + } |
| 13437 | + |
| 13438 | + r = mgr->apply(mgr); |
| 13439 | + if (r) { |
| 13440 | + DSSERR("failed to apply dispc config\n"); |
| 13441 | + goto put_device; |
| 13442 | + } |
| 13443 | + } |
| 13444 | + |
| 13445 | +put_device: |
| 13446 | + if (dssdev) |
| 13447 | + omap_dss_put_device(dssdev); |
| 13448 | + |
| 13449 | + return r ? r : size; |
| 13450 | +} |
| 13451 | + |
| 13452 | +static ssize_t manager_default_color_show(struct omap_overlay_manager *mgr, |
| 13453 | + char *buf) |
| 13454 | +{ |
| 13455 | + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.default_color); |
| 13456 | +} |
| 13457 | + |
| 13458 | +static ssize_t manager_default_color_store(struct omap_overlay_manager *mgr, |
| 13459 | + const char *buf, size_t size) |
| 13460 | +{ |
| 13461 | + struct omap_overlay_manager_info info; |
| 13462 | + u32 color; |
| 13463 | + int r; |
| 13464 | + |
| 13465 | + if (sscanf(buf, "%d", &color) != 1) |
| 13466 | + return -EINVAL; |
| 13467 | + |
| 13468 | + mgr->get_manager_info(mgr, &info); |
| 13469 | + |
| 13470 | + info.default_color = color; |
| 13471 | + |
| 13472 | + r = mgr->set_manager_info(mgr, &info); |
| 13473 | + if (r) |
| 13474 | + return r; |
| 13475 | + |
| 13476 | + r = mgr->apply(mgr); |
| 13477 | + if (r) |
| 13478 | + return r; |
| 13479 | + |
| 13480 | + return size; |
| 13481 | +} |
| 13482 | + |
| 13483 | +static const char *trans_key_type_str[] = { |
| 13484 | + "gfx-destination", |
| 13485 | + "video-source", |
| 13486 | +}; |
| 13487 | + |
| 13488 | +static ssize_t manager_trans_key_type_show(struct omap_overlay_manager *mgr, |
| 13489 | + char *buf) |
| 13490 | +{ |
| 13491 | + enum omap_dss_trans_key_type key_type; |
| 13492 | + |
| 13493 | + key_type = mgr->info.trans_key_type; |
| 13494 | + BUG_ON(key_type >= ARRAY_SIZE(trans_key_type_str)); |
| 13495 | + |
| 13496 | + return snprintf(buf, PAGE_SIZE, "%s\n", trans_key_type_str[key_type]); |
| 13497 | +} |
| 13498 | + |
| 13499 | +static ssize_t manager_trans_key_type_store(struct omap_overlay_manager *mgr, |
| 13500 | + const char *buf, size_t size) |
| 13501 | +{ |
| 13502 | + enum omap_dss_trans_key_type key_type; |
| 13503 | + struct omap_overlay_manager_info info; |
| 13504 | + int r; |
| 13505 | + |
| 13506 | + for (key_type = OMAP_DSS_COLOR_KEY_GFX_DST; |
| 13507 | + key_type < ARRAY_SIZE(trans_key_type_str); key_type++) { |
| 13508 | + if (sysfs_streq(buf, trans_key_type_str[key_type])) |
| 13509 | + break; |
| 13510 | + } |
| 13511 | + |
| 13512 | + if (key_type == ARRAY_SIZE(trans_key_type_str)) |
| 13513 | + return -EINVAL; |
| 13514 | + |
| 13515 | + mgr->get_manager_info(mgr, &info); |
| 13516 | + |
| 13517 | + info.trans_key_type = key_type; |
| 13518 | + |
| 13519 | + r = mgr->set_manager_info(mgr, &info); |
| 13520 | + if (r) |
| 13521 | + return r; |
| 13522 | + |
| 13523 | + r = mgr->apply(mgr); |
| 13524 | + if (r) |
| 13525 | + return r; |
| 13526 | + |
| 13527 | + return size; |
| 13528 | +} |
| 13529 | + |
| 13530 | +static ssize_t manager_trans_key_value_show(struct omap_overlay_manager *mgr, |
| 13531 | + char *buf) |
| 13532 | +{ |
| 13533 | + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_key); |
| 13534 | +} |
| 13535 | + |
| 13536 | +static ssize_t manager_trans_key_value_store(struct omap_overlay_manager *mgr, |
| 13537 | + const char *buf, size_t size) |
| 13538 | +{ |
| 13539 | + struct omap_overlay_manager_info info; |
| 13540 | + u32 key_value; |
| 13541 | + int r; |
| 13542 | + |
| 13543 | + if (sscanf(buf, "%d", &key_value) != 1) |
| 13544 | + return -EINVAL; |
| 13545 | + |
| 13546 | + mgr->get_manager_info(mgr, &info); |
| 13547 | + |
| 13548 | + info.trans_key = key_value; |
| 13549 | + |
| 13550 | + r = mgr->set_manager_info(mgr, &info); |
| 13551 | + if (r) |
| 13552 | + return r; |
| 13553 | + |
| 13554 | + r = mgr->apply(mgr); |
| 13555 | + if (r) |
| 13556 | + return r; |
| 13557 | + |
| 13558 | + return size; |
| 13559 | +} |
| 13560 | + |
| 13561 | +static ssize_t manager_trans_key_enabled_show(struct omap_overlay_manager *mgr, |
| 13562 | + char *buf) |
| 13563 | +{ |
| 13564 | + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.trans_enabled); |
| 13565 | +} |
| 13566 | + |
| 13567 | +static ssize_t manager_trans_key_enabled_store(struct omap_overlay_manager *mgr, |
| 13568 | + const char *buf, size_t size) |
| 13569 | +{ |
| 13570 | + struct omap_overlay_manager_info info; |
| 13571 | + int enable; |
| 13572 | + int r; |
| 13573 | + |
| 13574 | + if (sscanf(buf, "%d", &enable) != 1) |
| 13575 | + return -EINVAL; |
| 13576 | + |
| 13577 | + mgr->get_manager_info(mgr, &info); |
| 13578 | + |
| 13579 | + info.trans_enabled = enable ? true : false; |
| 13580 | + |
| 13581 | + r = mgr->set_manager_info(mgr, &info); |
| 13582 | + if (r) |
| 13583 | + return r; |
| 13584 | + |
| 13585 | + r = mgr->apply(mgr); |
| 13586 | + if (r) |
| 13587 | + return r; |
| 13588 | + |
| 13589 | + return size; |
| 13590 | +} |
| 13591 | + |
| 13592 | +static ssize_t manager_alpha_blending_enabled_show( |
| 13593 | + struct omap_overlay_manager *mgr, char *buf) |
| 13594 | +{ |
| 13595 | + return snprintf(buf, PAGE_SIZE, "%d\n", mgr->info.alpha_enabled); |
| 13596 | +} |
| 13597 | + |
| 13598 | +static ssize_t manager_alpha_blending_enabled_store( |
| 13599 | + struct omap_overlay_manager *mgr, |
| 13600 | + const char *buf, size_t size) |
| 13601 | +{ |
| 13602 | + struct omap_overlay_manager_info info; |
| 13603 | + int enable; |
| 13604 | + int r; |
| 13605 | + |
| 13606 | + if (sscanf(buf, "%d", &enable) != 1) |
| 13607 | + return -EINVAL; |
| 13608 | + |
| 13609 | + mgr->get_manager_info(mgr, &info); |
| 13610 | + |
| 13611 | + info.alpha_enabled = enable ? true : false; |
| 13612 | + |
| 13613 | + r = mgr->set_manager_info(mgr, &info); |
| 13614 | + if (r) |
| 13615 | + return r; |
| 13616 | + |
| 13617 | + r = mgr->apply(mgr); |
| 13618 | + if (r) |
| 13619 | + return r; |
| 13620 | + |
| 13621 | + return size; |
| 13622 | +} |
| 13623 | + |
| 13624 | +struct manager_attribute { |
| 13625 | + struct attribute attr; |
| 13626 | + ssize_t (*show)(struct omap_overlay_manager *, char *); |
| 13627 | + ssize_t (*store)(struct omap_overlay_manager *, const char *, size_t); |
| 13628 | +}; |
| 13629 | + |
| 13630 | +#define MANAGER_ATTR(_name, _mode, _show, _store) \ |
| 13631 | + struct manager_attribute manager_attr_##_name = \ |
| 13632 | + __ATTR(_name, _mode, _show, _store) |
| 13633 | + |
| 13634 | +static MANAGER_ATTR(name, S_IRUGO, manager_name_show, NULL); |
| 13635 | +static MANAGER_ATTR(display, S_IRUGO|S_IWUSR, |
| 13636 | + manager_display_show, manager_display_store); |
| 13637 | +static MANAGER_ATTR(default_color, S_IRUGO|S_IWUSR, |
| 13638 | + manager_default_color_show, manager_default_color_store); |
| 13639 | +static MANAGER_ATTR(trans_key_type, S_IRUGO|S_IWUSR, |
| 13640 | + manager_trans_key_type_show, manager_trans_key_type_store); |
| 13641 | +static MANAGER_ATTR(trans_key_value, S_IRUGO|S_IWUSR, |
| 13642 | + manager_trans_key_value_show, manager_trans_key_value_store); |
| 13643 | +static MANAGER_ATTR(trans_key_enabled, S_IRUGO|S_IWUSR, |
| 13644 | + manager_trans_key_enabled_show, |
| 13645 | + manager_trans_key_enabled_store); |
| 13646 | +static MANAGER_ATTR(alpha_blending_enabled, S_IRUGO|S_IWUSR, |
| 13647 | + manager_alpha_blending_enabled_show, |
| 13648 | + manager_alpha_blending_enabled_store); |
| 13649 | + |
| 13650 | + |
| 13651 | +static struct attribute *manager_sysfs_attrs[] = { |
| 13652 | + &manager_attr_name.attr, |
| 13653 | + &manager_attr_display.attr, |
| 13654 | + &manager_attr_default_color.attr, |
| 13655 | + &manager_attr_trans_key_type.attr, |
| 13656 | + &manager_attr_trans_key_value.attr, |
| 13657 | + &manager_attr_trans_key_enabled.attr, |
| 13658 | + &manager_attr_alpha_blending_enabled.attr, |
| 13659 | + NULL |
| 13660 | +}; |
| 13661 | + |
| 13662 | +static ssize_t manager_attr_show(struct kobject *kobj, struct attribute *attr, |
| 13663 | + char *buf) |
| 13664 | +{ |
| 13665 | + struct omap_overlay_manager *manager; |
| 13666 | + struct manager_attribute *manager_attr; |
| 13667 | + |
| 13668 | + manager = container_of(kobj, struct omap_overlay_manager, kobj); |
| 13669 | + manager_attr = container_of(attr, struct manager_attribute, attr); |
| 13670 | + |
| 13671 | + if (!manager_attr->show) |
| 13672 | + return -ENOENT; |
| 13673 | + |
| 13674 | + return manager_attr->show(manager, buf); |
| 13675 | +} |
| 13676 | + |
| 13677 | +static ssize_t manager_attr_store(struct kobject *kobj, struct attribute *attr, |
| 13678 | + const char *buf, size_t size) |
| 13679 | +{ |
| 13680 | + struct omap_overlay_manager *manager; |
| 13681 | + struct manager_attribute *manager_attr; |
| 13682 | + |
| 13683 | + manager = container_of(kobj, struct omap_overlay_manager, kobj); |
| 13684 | + manager_attr = container_of(attr, struct manager_attribute, attr); |
| 13685 | + |
| 13686 | + if (!manager_attr->store) |
| 13687 | + return -ENOENT; |
| 13688 | + |
| 13689 | + return manager_attr->store(manager, buf, size); |
| 13690 | +} |
| 13691 | + |
| 13692 | +static struct sysfs_ops manager_sysfs_ops = { |
| 13693 | + .show = manager_attr_show, |
| 13694 | + .store = manager_attr_store, |
| 13695 | +}; |
| 13696 | + |
| 13697 | +static struct kobj_type manager_ktype = { |
| 13698 | + .sysfs_ops = &manager_sysfs_ops, |
| 13699 | + .default_attrs = manager_sysfs_attrs, |
| 13700 | +}; |
| 13701 | + |
| 13702 | +/* |
| 13703 | + * We have 4 levels of cache for the dispc settings. First two are in SW and |
| 13704 | + * the latter two in HW. |
| 13705 | + * |
| 13706 | + * +--------------------+ |
| 13707 | + * |overlay/manager_info| |
| 13708 | + * +--------------------+ |
| 13709 | + * v |
| 13710 | + * apply() |
| 13711 | + * v |
| 13712 | + * +--------------------+ |
| 13713 | + * | dss_cache | |
| 13714 | + * +--------------------+ |
| 13715 | + * v |
| 13716 | + * configure() |
| 13717 | + * v |
| 13718 | + * +--------------------+ |
| 13719 | + * | shadow registers | |
| 13720 | + * +--------------------+ |
| 13721 | + * v |
| 13722 | + * VFP or lcd/digit_enable |
| 13723 | + * v |
| 13724 | + * +--------------------+ |
| 13725 | + * | registers | |
| 13726 | + * +--------------------+ |
| 13727 | + */ |
| 13728 | + |
| 13729 | +struct overlay_cache_data { |
| 13730 | + /* If true, cache changed, but not written to shadow registers. Set |
| 13731 | + * in apply(), cleared when registers written. */ |
| 13732 | + bool dirty; |
| 13733 | + /* If true, shadow registers contain changed values not yet in real |
| 13734 | + * registers. Set when writing to shadow registers, cleared at |
| 13735 | + * VSYNC/EVSYNC */ |
| 13736 | + bool shadow_dirty; |
| 13737 | + |
| 13738 | + bool enabled; |
| 13739 | + |
| 13740 | + u32 paddr; |
| 13741 | + void __iomem *vaddr; |
| 13742 | + u16 screen_width; |
| 13743 | + u16 width; |
| 13744 | + u16 height; |
| 13745 | + enum omap_color_mode color_mode; |
| 13746 | + u8 rotation; |
| 13747 | + enum omap_dss_rotation_type rotation_type; |
| 13748 | + bool mirror; |
| 13749 | + |
| 13750 | + u16 pos_x; |
| 13751 | + u16 pos_y; |
| 13752 | + u16 out_width; /* if 0, out_width == width */ |
| 13753 | + u16 out_height; /* if 0, out_height == height */ |
| 13754 | + u8 global_alpha; |
| 13755 | + |
| 13756 | + enum omap_channel channel; |
| 13757 | + bool replication; |
| 13758 | + bool ilace; |
| 13759 | + |
| 13760 | + enum omap_burst_size burst_size; |
| 13761 | + u32 fifo_low; |
| 13762 | + u32 fifo_high; |
| 13763 | + |
| 13764 | + bool manual_update; |
| 13765 | +}; |
| 13766 | + |
| 13767 | +struct manager_cache_data { |
| 13768 | + /* If true, cache changed, but not written to shadow registers. Set |
| 13769 | + * in apply(), cleared when registers written. */ |
| 13770 | + bool dirty; |
| 13771 | + /* If true, shadow registers contain changed values not yet in real |
| 13772 | + * registers. Set when writing to shadow registers, cleared at |
| 13773 | + * VSYNC/EVSYNC */ |
| 13774 | + bool shadow_dirty; |
| 13775 | + |
| 13776 | + u32 default_color; |
| 13777 | + |
| 13778 | + enum omap_dss_trans_key_type trans_key_type; |
| 13779 | + u32 trans_key; |
| 13780 | + bool trans_enabled; |
| 13781 | + |
| 13782 | + bool alpha_enabled; |
| 13783 | + |
| 13784 | + bool manual_upd_display; |
| 13785 | + bool manual_update; |
| 13786 | + bool do_manual_update; |
| 13787 | + |
| 13788 | + /* manual update region */ |
| 13789 | + u16 x, y, w, h; |
| 13790 | +}; |
| 13791 | + |
| 13792 | +static struct { |
| 13793 | + spinlock_t lock; |
| 13794 | + struct overlay_cache_data overlay_cache[3]; |
| 13795 | + struct manager_cache_data manager_cache[2]; |
| 13796 | + |
| 13797 | + bool irq_enabled; |
| 13798 | +} dss_cache; |
| 13799 | + |
| 13800 | + |
| 13801 | + |
| 13802 | +static int omap_dss_set_device(struct omap_overlay_manager *mgr, |
| 13803 | + struct omap_dss_device *dssdev) |
| 13804 | +{ |
| 13805 | + int i; |
| 13806 | + int r; |
| 13807 | + |
| 13808 | + if (dssdev->manager) { |
| 13809 | + DSSERR("display '%s' already has a manager '%s'\n", |
| 13810 | + dssdev->name, dssdev->manager->name); |
| 13811 | + return -EINVAL; |
| 13812 | + } |
| 13813 | + |
| 13814 | + if ((mgr->supported_displays & dssdev->type) == 0) { |
| 13815 | + DSSERR("display '%s' does not support manager '%s'\n", |
| 13816 | + dssdev->name, mgr->name); |
| 13817 | + return -EINVAL; |
| 13818 | + } |
| 13819 | + |
| 13820 | + for (i = 0; i < mgr->num_overlays; i++) { |
| 13821 | + struct omap_overlay *ovl = mgr->overlays[i]; |
| 13822 | + |
| 13823 | + if (ovl->manager != mgr || !ovl->info.enabled) |
| 13824 | + continue; |
| 13825 | + |
| 13826 | + r = dss_check_overlay(ovl, dssdev); |
| 13827 | + if (r) |
| 13828 | + return r; |
| 13829 | + } |
| 13830 | + |
| 13831 | + dssdev->manager = mgr; |
| 13832 | + mgr->device = dssdev; |
| 13833 | + mgr->device_changed = true; |
| 13834 | + |
| 13835 | + return 0; |
| 13836 | +} |
| 13837 | + |
| 13838 | +static int omap_dss_unset_device(struct omap_overlay_manager *mgr) |
| 13839 | +{ |
| 13840 | + if (!mgr->device) { |
| 13841 | + DSSERR("failed to unset display, display not set.\n"); |
| 13842 | + return -EINVAL; |
| 13843 | + } |
| 13844 | + |
| 13845 | + mgr->device->manager = NULL; |
| 13846 | + mgr->device = NULL; |
| 13847 | + mgr->device_changed = true; |
| 13848 | + |
| 13849 | + return 0; |
| 13850 | +} |
| 13851 | + |
| 13852 | +static int dss_mgr_wait_for_go(struct omap_overlay_manager *mgr) |
| 13853 | +{ |
| 13854 | + unsigned long timeout = msecs_to_jiffies(500); |
| 13855 | + struct manager_cache_data *mc; |
| 13856 | + enum omap_channel channel; |
| 13857 | + u32 irq; |
| 13858 | + int r; |
| 13859 | + int i; |
| 13860 | + |
| 13861 | + if (!mgr->device) |
| 13862 | + return 0; |
| 13863 | + |
| 13864 | + if (mgr->device->type == OMAP_DISPLAY_TYPE_VENC) { |
| 13865 | + irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; |
| 13866 | + channel = OMAP_DSS_CHANNEL_DIGIT; |
| 13867 | + } else { |
| 13868 | + if (mgr->device->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { |
| 13869 | + enum omap_dss_update_mode mode; |
| 13870 | + mode = mgr->device->get_update_mode(mgr->device); |
| 13871 | + if (mode != OMAP_DSS_UPDATE_AUTO) |
| 13872 | + return 0; |
| 13873 | + |
| 13874 | + irq = DISPC_IRQ_FRAMEDONE; |
| 13875 | + } else { |
| 13876 | + irq = DISPC_IRQ_VSYNC; |
| 13877 | + } |
| 13878 | + channel = OMAP_DSS_CHANNEL_LCD; |
| 13879 | + } |
| 13880 | + |
| 13881 | + mc = &dss_cache.manager_cache[mgr->id]; |
| 13882 | + i = 0; |
| 13883 | + while (1) { |
| 13884 | + unsigned long flags; |
| 13885 | + bool shadow_dirty, dirty; |
| 13886 | + |
| 13887 | + spin_lock_irqsave(&dss_cache.lock, flags); |
| 13888 | + dirty = mc->dirty; |
| 13889 | + shadow_dirty = mc->shadow_dirty; |
| 13890 | + spin_unlock_irqrestore(&dss_cache.lock, flags); |
| 13891 | + |
| 13892 | + if (!dirty && !shadow_dirty) { |
| 13893 | + r = 0; |
| 13894 | + break; |
| 13895 | + } |
| 13896 | + |
| 13897 | + /* 4 iterations is the worst case: |
| 13898 | + * 1 - initial iteration, dirty = true (between VFP and VSYNC) |
| 13899 | + * 2 - first VSYNC, dirty = true |
| 13900 | + * 3 - dirty = false, shadow_dirty = true |
| 13901 | + * 4 - shadow_dirty = false */ |
| 13902 | + if (i++ == 3) { |
| 13903 | + DSSERR("mgr(%d)->wait_for_go() not finishing\n", |
| 13904 | + mgr->id); |
| 13905 | + r = 0; |
| 13906 | + break; |
| 13907 | + } |
| 13908 | + |
| 13909 | + r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); |
| 13910 | + if (r == -ERESTARTSYS) |
| 13911 | + break; |
| 13912 | + |
| 13913 | + if (r) { |
| 13914 | + DSSERR("mgr(%d)->wait_for_go() timeout\n", mgr->id); |
| 13915 | + break; |
| 13916 | + } |
| 13917 | + } |
| 13918 | + |
| 13919 | + return r; |
| 13920 | +} |
| 13921 | + |
| 13922 | +int dss_mgr_wait_for_go_ovl(struct omap_overlay *ovl) |
| 13923 | +{ |
| 13924 | + unsigned long timeout = msecs_to_jiffies(500); |
| 13925 | + enum omap_channel channel; |
| 13926 | + struct overlay_cache_data *oc; |
| 13927 | + struct omap_dss_device *dssdev; |
| 13928 | + u32 irq; |
| 13929 | + int r; |
| 13930 | + int i; |
| 13931 | + |
| 13932 | + if (!ovl->manager || !ovl->manager->device) |
| 13933 | + return 0; |
| 13934 | + |
| 13935 | + dssdev = ovl->manager->device; |
| 13936 | + |
| 13937 | + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { |
| 13938 | + irq = DISPC_IRQ_EVSYNC_ODD | DISPC_IRQ_EVSYNC_EVEN; |
| 13939 | + channel = OMAP_DSS_CHANNEL_DIGIT; |
| 13940 | + } else { |
| 13941 | + if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { |
| 13942 | + enum omap_dss_update_mode mode; |
| 13943 | + mode = dssdev->get_update_mode(dssdev); |
| 13944 | + if (mode != OMAP_DSS_UPDATE_AUTO) |
| 13945 | + return 0; |
| 13946 | + |
| 13947 | + irq = DISPC_IRQ_FRAMEDONE; |
| 13948 | + } else { |
| 13949 | + irq = DISPC_IRQ_VSYNC; |
| 13950 | + } |
| 13951 | + channel = OMAP_DSS_CHANNEL_LCD; |
| 13952 | + } |
| 13953 | + |
| 13954 | + oc = &dss_cache.overlay_cache[ovl->id]; |
| 13955 | + i = 0; |
| 13956 | + while (1) { |
| 13957 | + unsigned long flags; |
| 13958 | + bool shadow_dirty, dirty; |
| 13959 | + |
| 13960 | + spin_lock_irqsave(&dss_cache.lock, flags); |
| 13961 | + dirty = oc->dirty; |
| 13962 | + shadow_dirty = oc->shadow_dirty; |
| 13963 | + spin_unlock_irqrestore(&dss_cache.lock, flags); |
| 13964 | + |
| 13965 | + if (!dirty && !shadow_dirty) { |
| 13966 | + r = 0; |
| 13967 | + break; |
| 13968 | + } |
| 13969 | + |
| 13970 | + /* 4 iterations is the worst case: |
| 13971 | + * 1 - initial iteration, dirty = true (between VFP and VSYNC) |
| 13972 | + * 2 - first VSYNC, dirty = true |
| 13973 | + * 3 - dirty = false, shadow_dirty = true |
| 13974 | + * 4 - shadow_dirty = false */ |
| 13975 | + if (i++ == 3) { |
| 13976 | + DSSERR("ovl(%d)->wait_for_go() not finishing\n", |
| 13977 | + ovl->id); |
| 13978 | + r = 0; |
| 13979 | + break; |
| 13980 | + } |
| 13981 | + |
| 13982 | + r = omap_dispc_wait_for_irq_interruptible_timeout(irq, timeout); |
| 13983 | + if (r == -ERESTARTSYS) |
| 13984 | + break; |
| 13985 | + |
| 13986 | + if (r) { |
| 13987 | + DSSERR("ovl(%d)->wait_for_go() timeout\n", ovl->id); |
| 13988 | + break; |
| 13989 | + } |
| 13990 | + } |
| 13991 | + |
| 13992 | + return r; |
| 13993 | +} |
| 13994 | + |
| 13995 | +static int overlay_enabled(struct omap_overlay *ovl) |
| 13996 | +{ |
| 13997 | + return ovl->info.enabled && ovl->manager && ovl->manager->device; |
| 13998 | +} |
| 13999 | + |
| 14000 | +/* Is rect1 a subset of rect2? */ |
| 14001 | +static bool rectangle_subset(int x1, int y1, int w1, int h1, |
| 14002 | + int x2, int y2, int w2, int h2) |
| 14003 | +{ |
| 14004 | + if (x1 < x2 || y1 < y2) |
| 14005 | + return false; |
| 14006 | + |
| 14007 | + if (x1 + w1 > x2 + w2) |
| 14008 | + return false; |
| 14009 | + |
| 14010 | + if (y1 + h1 > y2 + h2) |
| 14011 | + return false; |
| 14012 | + |
| 14013 | + return true; |
| 14014 | +} |
| 14015 | + |
| 14016 | +/* Do rect1 and rect2 overlap? */ |
| 14017 | +static bool rectangle_intersects(int x1, int y1, int w1, int h1, |
| 14018 | + int x2, int y2, int w2, int h2) |
| 14019 | +{ |
| 14020 | + if (x1 >= x2 + w2) |
| 14021 | + return false; |
| 14022 | + |
| 14023 | + if (x2 >= x1 + w1) |
| 14024 | + return false; |
| 14025 | + |
| 14026 | + if (y1 >= y2 + h2) |
| 14027 | + return false; |
| 14028 | + |
| 14029 | + if (y2 >= y1 + h1) |
| 14030 | + return false; |
| 14031 | + |
| 14032 | + return true; |
| 14033 | +} |
| 14034 | + |
| 14035 | +static bool dispc_is_overlay_scaled(struct overlay_cache_data *oc) |
| 14036 | +{ |
| 14037 | + if (oc->out_width != 0 && oc->width != oc->out_width) |
| 14038 | + return true; |
| 14039 | + |
| 14040 | + if (oc->out_height != 0 && oc->height != oc->out_height) |
| 14041 | + return true; |
| 14042 | + |
| 14043 | + return false; |
| 14044 | +} |
| 14045 | + |
| 14046 | +static int configure_overlay(enum omap_plane plane) |
| 14047 | +{ |
| 14048 | + struct overlay_cache_data *c; |
| 14049 | + struct manager_cache_data *mc; |
| 14050 | + u16 outw, outh; |
| 14051 | + u16 x, y, w, h; |
| 14052 | + u32 paddr; |
| 14053 | + int r; |
| 14054 | + |
| 14055 | + DSSDBGF("%d", plane); |
| 14056 | + |
| 14057 | + c = &dss_cache.overlay_cache[plane]; |
| 14058 | + |
| 14059 | + if (!c->enabled) { |
| 14060 | + dispc_enable_plane(plane, 0); |
| 14061 | + return 0; |
| 14062 | + } |
| 14063 | + |
| 14064 | + mc = &dss_cache.manager_cache[c->channel]; |
| 14065 | + |
| 14066 | + x = c->pos_x; |
| 14067 | + y = c->pos_y; |
| 14068 | + w = c->width; |
| 14069 | + h = c->height; |
| 14070 | + outw = c->out_width == 0 ? c->width : c->out_width; |
| 14071 | + outh = c->out_height == 0 ? c->height : c->out_height; |
| 14072 | + paddr = c->paddr; |
| 14073 | + |
| 14074 | + if (c->manual_update && mc->do_manual_update) { |
| 14075 | + unsigned bpp; |
| 14076 | + /* If the overlay is outside the update region, disable it */ |
| 14077 | + if (!rectangle_intersects(mc->x, mc->y, mc->w, mc->h, |
| 14078 | + x, y, outw, outh)) { |
| 14079 | + dispc_enable_plane(plane, 0); |
| 14080 | + return 0; |
| 14081 | + } |
| 14082 | + |
| 14083 | + switch (c->color_mode) { |
| 14084 | + case OMAP_DSS_COLOR_RGB16: |
| 14085 | + case OMAP_DSS_COLOR_ARGB16: |
| 14086 | + case OMAP_DSS_COLOR_YUV2: |
| 14087 | + case OMAP_DSS_COLOR_UYVY: |
| 14088 | + bpp = 16; |
| 14089 | + break; |
| 14090 | + |
| 14091 | + case OMAP_DSS_COLOR_RGB24P: |
| 14092 | + bpp = 24; |
| 14093 | + break; |
| 14094 | + |
| 14095 | + case OMAP_DSS_COLOR_RGB24U: |
| 14096 | + case OMAP_DSS_COLOR_ARGB32: |
| 14097 | + case OMAP_DSS_COLOR_RGBA32: |
| 14098 | + case OMAP_DSS_COLOR_RGBX32: |
| 14099 | + bpp = 32; |
| 14100 | + break; |
| 14101 | + |
| 14102 | + default: |
| 14103 | + BUG(); |
| 14104 | + } |
| 14105 | + |
| 14106 | + if (dispc_is_overlay_scaled(c)) { |
| 14107 | + /* If the overlay is scaled, the update area has |
| 14108 | + * already been enlarged to cover the whole overlay. We |
| 14109 | + * only need to adjust x/y here */ |
| 14110 | + x = c->pos_x - mc->x; |
| 14111 | + y = c->pos_y - mc->y; |
| 14112 | + } else { |
| 14113 | + if (mc->x > c->pos_x) { |
| 14114 | + x = 0; |
| 14115 | + w -= (mc->x - c->pos_x); |
| 14116 | + paddr += (mc->x - c->pos_x) * bpp / 8; |
| 14117 | + } else { |
| 14118 | + x = c->pos_x - mc->x; |
| 14119 | + } |
| 14120 | + |
| 14121 | + if (mc->y > c->pos_y) { |
| 14122 | + y = 0; |
| 14123 | + h -= (mc->y - c->pos_y); |
| 14124 | + paddr += (mc->y - c->pos_y) * c->screen_width * |
| 14125 | + bpp / 8; |
| 14126 | + } else { |
| 14127 | + y = c->pos_y - mc->y; |
| 14128 | + } |
| 14129 | + |
| 14130 | + if (mc->w < (x+w)) |
| 14131 | + w -= (x+w) - (mc->w); |
| 14132 | + |
| 14133 | + if (mc->h < (y+h)) |
| 14134 | + h -= (y+h) - (mc->h); |
| 14135 | + |
| 14136 | + outw = w; |
| 14137 | + outh = h; |
| 14138 | + } |
| 14139 | + } |
| 14140 | + |
| 14141 | + r = dispc_setup_plane(plane, |
| 14142 | + paddr, |
| 14143 | + c->screen_width, |
| 14144 | + x, y, |
| 14145 | + w, h, |
| 14146 | + outw, outh, |
| 14147 | + c->color_mode, |
| 14148 | + c->ilace, |
| 14149 | + c->rotation_type, |
| 14150 | + c->rotation, |
| 14151 | + c->mirror, |
| 14152 | + c->global_alpha); |
| 14153 | + |
| 14154 | + if (r) { |
| 14155 | + /* this shouldn't happen */ |
| 14156 | + DSSERR("dispc_setup_plane failed for ovl %d\n", plane); |
| 14157 | + dispc_enable_plane(plane, 0); |
| 14158 | + return r; |
| 14159 | + } |
| 14160 | + |
| 14161 | + dispc_enable_replication(plane, c->replication); |
| 14162 | + |
| 14163 | + dispc_set_burst_size(plane, c->burst_size); |
| 14164 | + dispc_setup_plane_fifo(plane, c->fifo_low, c->fifo_high); |
| 14165 | + |
| 14166 | + dispc_enable_plane(plane, 1); |
| 14167 | + |
| 14168 | + return 0; |
| 14169 | +} |
| 14170 | + |
| 14171 | +static void configure_manager(enum omap_channel channel) |
| 14172 | +{ |
| 14173 | + struct manager_cache_data *c; |
| 14174 | + |
| 14175 | + DSSDBGF("%d", channel); |
| 14176 | + |
| 14177 | + c = &dss_cache.manager_cache[channel]; |
| 14178 | + |
| 14179 | + dispc_set_trans_key(channel, c->trans_key_type, c->trans_key); |
| 14180 | + dispc_enable_trans_key(channel, c->trans_enabled); |
| 14181 | + dispc_enable_alpha_blending(channel, c->alpha_enabled); |
| 14182 | +} |
| 14183 | + |
| 14184 | +/* configure_dispc() tries to write values from cache to shadow registers. |
| 14185 | + * It writes only to those managers/overlays that are not busy. |
| 14186 | + * returns 0 if everything could be written to shadow registers. |
| 14187 | + * returns 1 if not everything could be written to shadow registers. */ |
| 14188 | +static int configure_dispc(void) |
| 14189 | +{ |
| 14190 | + struct overlay_cache_data *oc; |
| 14191 | + struct manager_cache_data *mc; |
| 14192 | + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); |
| 14193 | + const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); |
| 14194 | + int i; |
| 14195 | + int r; |
| 14196 | + bool mgr_busy[2]; |
| 14197 | + bool mgr_go[2]; |
| 14198 | + bool busy; |
| 14199 | + |
| 14200 | + r = 0; |
| 14201 | + busy = false; |
| 14202 | + |
| 14203 | + mgr_busy[0] = dispc_go_busy(0); |
| 14204 | + mgr_busy[1] = dispc_go_busy(1); |
| 14205 | + mgr_go[0] = false; |
| 14206 | + mgr_go[1] = false; |
| 14207 | + |
| 14208 | + /* Commit overlay settings */ |
| 14209 | + for (i = 0; i < num_ovls; ++i) { |
| 14210 | + oc = &dss_cache.overlay_cache[i]; |
| 14211 | + mc = &dss_cache.manager_cache[oc->channel]; |
| 14212 | + |
| 14213 | + if (!oc->dirty) |
| 14214 | + continue; |
| 14215 | + |
| 14216 | + if (oc->manual_update && !mc->do_manual_update) |
| 14217 | + continue; |
| 14218 | + |
| 14219 | + if (mgr_busy[oc->channel]) { |
| 14220 | + busy = true; |
| 14221 | + continue; |
| 14222 | + } |
| 14223 | + |
| 14224 | + r = configure_overlay(i); |
| 14225 | + if (r) |
| 14226 | + DSSERR("configure_overlay %d failed\n", i); |
| 14227 | + |
| 14228 | + oc->dirty = false; |
| 14229 | + oc->shadow_dirty = true; |
| 14230 | + mgr_go[oc->channel] = true; |
| 14231 | + } |
| 14232 | + |
| 14233 | + /* Commit manager settings */ |
| 14234 | + for (i = 0; i < num_mgrs; ++i) { |
| 14235 | + mc = &dss_cache.manager_cache[i]; |
| 14236 | + |
| 14237 | + if (!mc->dirty) |
| 14238 | + continue; |
| 14239 | + |
| 14240 | + if (mc->manual_update && !mc->do_manual_update) |
| 14241 | + continue; |
| 14242 | + |
| 14243 | + if (mgr_busy[i]) { |
| 14244 | + busy = true; |
| 14245 | + continue; |
| 14246 | + } |
| 14247 | + |
| 14248 | + configure_manager(i); |
| 14249 | + mc->dirty = false; |
| 14250 | + mc->shadow_dirty = true; |
| 14251 | + mgr_go[i] = true; |
| 14252 | + } |
| 14253 | + |
| 14254 | + /* set GO */ |
| 14255 | + for (i = 0; i < num_mgrs; ++i) { |
| 14256 | + mc = &dss_cache.manager_cache[i]; |
| 14257 | + |
| 14258 | + if (!mgr_go[i]) |
| 14259 | + continue; |
| 14260 | + |
| 14261 | + /* We don't need GO with manual update display. LCD iface will |
| 14262 | + * always be turned off after frame, and new settings will be |
| 14263 | + * taken in to use at next update */ |
| 14264 | + if (!mc->manual_upd_display) |
| 14265 | + dispc_go(i); |
| 14266 | + } |
| 14267 | + |
| 14268 | + if (busy) |
| 14269 | + r = 1; |
| 14270 | + else |
| 14271 | + r = 0; |
| 14272 | + |
| 14273 | + return r; |
| 14274 | +} |
| 14275 | + |
| 14276 | +/* Configure dispc for partial update. Return possibly modified update |
| 14277 | + * area */ |
| 14278 | +void dss_setup_partial_planes(struct omap_dss_device *dssdev, |
| 14279 | + u16 *xi, u16 *yi, u16 *wi, u16 *hi) |
| 14280 | +{ |
| 14281 | + struct overlay_cache_data *oc; |
| 14282 | + struct manager_cache_data *mc; |
| 14283 | + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); |
| 14284 | + struct omap_overlay_manager *mgr; |
| 14285 | + int i; |
| 14286 | + u16 x, y, w, h; |
| 14287 | + unsigned long flags; |
| 14288 | + |
| 14289 | + x = *xi; |
| 14290 | + y = *yi; |
| 14291 | + w = *wi; |
| 14292 | + h = *hi; |
| 14293 | + |
| 14294 | + DSSDBG("dispc_setup_partial_planes %d,%d %dx%d\n", |
| 14295 | + *xi, *yi, *wi, *hi); |
| 14296 | + |
| 14297 | + mgr = dssdev->manager; |
| 14298 | + |
| 14299 | + if (!mgr) { |
| 14300 | + DSSDBG("no manager\n"); |
| 14301 | + return; |
| 14302 | + } |
| 14303 | + |
| 14304 | + spin_lock_irqsave(&dss_cache.lock, flags); |
| 14305 | + |
| 14306 | + /* We need to show the whole overlay if it is scaled. So look for |
| 14307 | + * those, and make the update area larger if found. |
| 14308 | + * Also mark the overlay cache dirty */ |
| 14309 | + for (i = 0; i < num_ovls; ++i) { |
| 14310 | + unsigned x1, y1, x2, y2; |
| 14311 | + unsigned outw, outh; |
| 14312 | + |
| 14313 | + oc = &dss_cache.overlay_cache[i]; |
| 14314 | + |
| 14315 | + if (oc->channel != mgr->id) |
| 14316 | + continue; |
| 14317 | + |
| 14318 | + oc->dirty = true; |
| 14319 | + |
| 14320 | + if (!oc->enabled) |
| 14321 | + continue; |
| 14322 | + |
| 14323 | + if (!dispc_is_overlay_scaled(oc)) |
| 14324 | + continue; |
| 14325 | + |
| 14326 | + outw = oc->out_width == 0 ? oc->width : oc->out_width; |
| 14327 | + outh = oc->out_height == 0 ? oc->height : oc->out_height; |
| 14328 | + |
| 14329 | + /* is the overlay outside the update region? */ |
| 14330 | + if (!rectangle_intersects(x, y, w, h, |
| 14331 | + oc->pos_x, oc->pos_y, |
| 14332 | + outw, outh)) |
| 14333 | + continue; |
| 14334 | + |
| 14335 | + /* if the overlay totally inside the update region? */ |
| 14336 | + if (rectangle_subset(oc->pos_x, oc->pos_y, outw, outh, |
| 14337 | + x, y, w, h)) |
| 14338 | + continue; |
| 14339 | + |
| 14340 | + if (x > oc->pos_x) |
| 14341 | + x1 = oc->pos_x; |
| 14342 | + else |
| 14343 | + x1 = x; |
| 14344 | + |
| 14345 | + if (y > oc->pos_y) |
| 14346 | + y1 = oc->pos_y; |
| 14347 | + else |
| 14348 | + y1 = y; |
| 14349 | + |
| 14350 | + if ((x + w) < (oc->pos_x + outw)) |
| 14351 | + x2 = oc->pos_x + outw; |
| 14352 | + else |
| 14353 | + x2 = x + w; |
| 14354 | + |
| 14355 | + if ((y + h) < (oc->pos_y + outh)) |
| 14356 | + y2 = oc->pos_y + outh; |
| 14357 | + else |
| 14358 | + y2 = y + h; |
| 14359 | + |
| 14360 | + x = x1; |
| 14361 | + y = y1; |
| 14362 | + w = x2 - x1; |
| 14363 | + h = y2 - y1; |
| 14364 | + |
| 14365 | + DSSDBG("changing upd area due to ovl(%d) scaling %d,%d %dx%d\n", |
| 14366 | + i, x, y, w, h); |
| 14367 | + } |
| 14368 | + |
| 14369 | + mc = &dss_cache.manager_cache[mgr->id]; |
| 14370 | + mc->do_manual_update = true; |
| 14371 | + mc->x = x; |
| 14372 | + mc->y = y; |
| 14373 | + mc->w = w; |
| 14374 | + mc->h = h; |
| 14375 | + |
| 14376 | + configure_dispc(); |
| 14377 | + |
| 14378 | + mc->do_manual_update = false; |
| 14379 | + |
| 14380 | + spin_unlock_irqrestore(&dss_cache.lock, flags); |
| 14381 | + |
| 14382 | + *xi = x; |
| 14383 | + *yi = y; |
| 14384 | + *wi = w; |
| 14385 | + *hi = h; |
| 14386 | +} |
| 14387 | + |
| 14388 | +void dss_start_update(struct omap_dss_device *dssdev) |
| 14389 | +{ |
| 14390 | + struct manager_cache_data *mc; |
| 14391 | + struct overlay_cache_data *oc; |
| 14392 | + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); |
| 14393 | + const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); |
| 14394 | + struct omap_overlay_manager *mgr; |
| 14395 | + int i; |
| 14396 | + |
| 14397 | + mgr = dssdev->manager; |
| 14398 | + |
| 14399 | + for (i = 0; i < num_ovls; ++i) { |
| 14400 | + oc = &dss_cache.overlay_cache[i]; |
| 14401 | + if (oc->channel != mgr->id) |
| 14402 | + continue; |
| 14403 | + |
| 14404 | + oc->shadow_dirty = false; |
| 14405 | + } |
| 14406 | + |
| 14407 | + for (i = 0; i < num_mgrs; ++i) { |
| 14408 | + mc = &dss_cache.manager_cache[i]; |
| 14409 | + if (mgr->id != i) |
| 14410 | + continue; |
| 14411 | + |
| 14412 | + mc->shadow_dirty = false; |
| 14413 | + } |
| 14414 | + |
| 14415 | + dispc_enable_lcd_out(1); |
| 14416 | +} |
| 14417 | + |
| 14418 | +static void dss_apply_irq_handler(void *data, u32 mask) |
| 14419 | +{ |
| 14420 | + struct manager_cache_data *mc; |
| 14421 | + struct overlay_cache_data *oc; |
| 14422 | + const int num_ovls = ARRAY_SIZE(dss_cache.overlay_cache); |
| 14423 | + const int num_mgrs = ARRAY_SIZE(dss_cache.manager_cache); |
| 14424 | + int i, r; |
| 14425 | + bool mgr_busy[2]; |
| 14426 | + |
| 14427 | + mgr_busy[0] = dispc_go_busy(0); |
| 14428 | + mgr_busy[1] = dispc_go_busy(1); |
| 14429 | + |
| 14430 | + spin_lock(&dss_cache.lock); |
| 14431 | + |
| 14432 | + for (i = 0; i < num_ovls; ++i) { |
| 14433 | + oc = &dss_cache.overlay_cache[i]; |
| 14434 | + if (!mgr_busy[oc->channel]) |
| 14435 | + oc->shadow_dirty = false; |
| 14436 | + } |
| 14437 | + |
| 14438 | + for (i = 0; i < num_mgrs; ++i) { |
| 14439 | + mc = &dss_cache.manager_cache[i]; |
| 14440 | + if (!mgr_busy[i]) |
| 14441 | + mc->shadow_dirty = false; |
| 14442 | + } |
| 14443 | + |
| 14444 | + r = configure_dispc(); |
| 14445 | + if (r == 1) |
| 14446 | + goto end; |
| 14447 | + |
| 14448 | + /* re-read busy flags */ |
| 14449 | + mgr_busy[0] = dispc_go_busy(0); |
| 14450 | + mgr_busy[1] = dispc_go_busy(1); |
| 14451 | + |
| 14452 | + /* keep running as long as there are busy managers, so that |
| 14453 | + * we can collect overlay-applied information */ |
| 14454 | + for (i = 0; i < num_mgrs; ++i) { |
| 14455 | + if (mgr_busy[i]) |
| 14456 | + goto end; |
| 14457 | + } |
| 14458 | + |
| 14459 | + omap_dispc_unregister_isr(dss_apply_irq_handler, NULL, |
| 14460 | + DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | |
| 14461 | + DISPC_IRQ_EVSYNC_EVEN); |
| 14462 | + dss_cache.irq_enabled = false; |
| 14463 | + |
| 14464 | +end: |
| 14465 | + spin_unlock(&dss_cache.lock); |
| 14466 | +} |
| 14467 | + |
| 14468 | +static int omap_dss_mgr_apply(struct omap_overlay_manager *mgr) |
| 14469 | +{ |
| 14470 | + struct overlay_cache_data *oc; |
| 14471 | + struct manager_cache_data *mc; |
| 14472 | + int i; |
| 14473 | + struct omap_overlay *ovl; |
| 14474 | + int num_planes_enabled = 0; |
| 14475 | + bool use_fifomerge; |
| 14476 | + unsigned long flags; |
| 14477 | + int r; |
| 14478 | + |
| 14479 | + DSSDBG("omap_dss_mgr_apply(%s)\n", mgr->name); |
| 14480 | + |
| 14481 | + spin_lock_irqsave(&dss_cache.lock, flags); |
| 14482 | + |
| 14483 | + /* Configure overlays */ |
| 14484 | + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { |
| 14485 | + struct omap_dss_device *dssdev; |
| 14486 | + |
| 14487 | + ovl = omap_dss_get_overlay(i); |
| 14488 | + |
| 14489 | + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) |
| 14490 | + continue; |
| 14491 | + |
| 14492 | + oc = &dss_cache.overlay_cache[ovl->id]; |
| 14493 | + |
| 14494 | + if (!overlay_enabled(ovl)) { |
| 14495 | + if (oc->enabled) { |
| 14496 | + oc->enabled = false; |
| 14497 | + oc->dirty = true; |
| 14498 | + } |
| 14499 | + continue; |
| 14500 | + } |
| 14501 | + |
| 14502 | + if (!ovl->info_dirty) { |
| 14503 | + if (oc->enabled) |
| 14504 | + ++num_planes_enabled; |
| 14505 | + continue; |
| 14506 | + } |
| 14507 | + |
| 14508 | + dssdev = ovl->manager->device; |
| 14509 | + |
| 14510 | + if (dss_check_overlay(ovl, dssdev)) { |
| 14511 | + if (oc->enabled) { |
| 14512 | + oc->enabled = false; |
| 14513 | + oc->dirty = true; |
| 14514 | + } |
| 14515 | + continue; |
| 14516 | + } |
| 14517 | + |
| 14518 | + ovl->info_dirty = false; |
| 14519 | + oc->dirty = true; |
| 14520 | + |
| 14521 | + oc->paddr = ovl->info.paddr; |
| 14522 | + oc->vaddr = ovl->info.vaddr; |
| 14523 | + oc->screen_width = ovl->info.screen_width; |
| 14524 | + oc->width = ovl->info.width; |
| 14525 | + oc->height = ovl->info.height; |
| 14526 | + oc->color_mode = ovl->info.color_mode; |
| 14527 | + oc->rotation = ovl->info.rotation; |
| 14528 | + oc->rotation_type = ovl->info.rotation_type; |
| 14529 | + oc->mirror = ovl->info.mirror; |
| 14530 | + oc->pos_x = ovl->info.pos_x; |
| 14531 | + oc->pos_y = ovl->info.pos_y; |
| 14532 | + oc->out_width = ovl->info.out_width; |
| 14533 | + oc->out_height = ovl->info.out_height; |
| 14534 | + oc->global_alpha = ovl->info.global_alpha; |
| 14535 | + |
| 14536 | + oc->replication = |
| 14537 | + dss_use_replication(dssdev, ovl->info.color_mode); |
| 14538 | + |
| 14539 | + oc->ilace = dssdev->type == OMAP_DISPLAY_TYPE_VENC; |
| 14540 | + |
| 14541 | + oc->channel = ovl->manager->id; |
| 14542 | + |
| 14543 | + oc->enabled = true; |
| 14544 | + |
| 14545 | + oc->manual_update = |
| 14546 | + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && |
| 14547 | + dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO; |
| 14548 | + |
| 14549 | + ++num_planes_enabled; |
| 14550 | + } |
| 14551 | + |
| 14552 | + /* Configure managers */ |
| 14553 | + list_for_each_entry(mgr, &manager_list, list) { |
| 14554 | + struct omap_dss_device *dssdev; |
| 14555 | + |
| 14556 | + if (!(mgr->caps & OMAP_DSS_OVL_MGR_CAP_DISPC)) |
| 14557 | + continue; |
| 14558 | + |
| 14559 | + mc = &dss_cache.manager_cache[mgr->id]; |
| 14560 | + |
| 14561 | + if (mgr->device_changed) { |
| 14562 | + mgr->device_changed = false; |
| 14563 | + mgr->info_dirty = true; |
| 14564 | + } |
| 14565 | + |
| 14566 | + if (!mgr->info_dirty) |
| 14567 | + continue; |
| 14568 | + |
| 14569 | + if (!mgr->device) |
| 14570 | + continue; |
| 14571 | + |
| 14572 | + dssdev = mgr->device; |
| 14573 | + |
| 14574 | + mgr->info_dirty = false; |
| 14575 | + mc->dirty = true; |
| 14576 | + |
| 14577 | + mc->default_color = mgr->info.default_color; |
| 14578 | + mc->trans_key_type = mgr->info.trans_key_type; |
| 14579 | + mc->trans_key = mgr->info.trans_key; |
| 14580 | + mc->trans_enabled = mgr->info.trans_enabled; |
| 14581 | + mc->alpha_enabled = mgr->info.alpha_enabled; |
| 14582 | + |
| 14583 | + mc->manual_upd_display = |
| 14584 | + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; |
| 14585 | + |
| 14586 | + mc->manual_update = |
| 14587 | + dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE && |
| 14588 | + dssdev->get_update_mode(dssdev) != OMAP_DSS_UPDATE_AUTO; |
| 14589 | + } |
| 14590 | + |
| 14591 | + /* XXX TODO: Try to get fifomerge working. The problem is that it |
| 14592 | + * affects both managers, not individually but at the same time. This |
| 14593 | + * means the change has to be well synchronized. I guess the proper way |
| 14594 | + * is to have a two step process for fifo merge: |
| 14595 | + * fifomerge enable: |
| 14596 | + * 1. disable other planes, leaving one plane enabled |
| 14597 | + * 2. wait until the planes are disabled on HW |
| 14598 | + * 3. config merged fifo thresholds, enable fifomerge |
| 14599 | + * fifomerge disable: |
| 14600 | + * 1. config unmerged fifo thresholds, disable fifomerge |
| 14601 | + * 2. wait until fifo changes are in HW |
| 14602 | + * 3. enable planes |
| 14603 | + */ |
| 14604 | + use_fifomerge = false; |
| 14605 | + |
| 14606 | + /* Configure overlay fifos */ |
| 14607 | + for (i = 0; i < omap_dss_get_num_overlays(); ++i) { |
| 14608 | + struct omap_dss_device *dssdev; |
| 14609 | + u32 size; |
| 14610 | + |
| 14611 | + ovl = omap_dss_get_overlay(i); |
| 14612 | + |
| 14613 | + if (!(ovl->caps & OMAP_DSS_OVL_CAP_DISPC)) |
| 14614 | + continue; |
| 14615 | + |
| 14616 | + oc = &dss_cache.overlay_cache[ovl->id]; |
| 14617 | + |
| 14618 | + if (!oc->enabled) |
| 14619 | + continue; |
| 14620 | + |
| 14621 | + dssdev = ovl->manager->device; |
| 14622 | + |
| 14623 | + size = dispc_get_plane_fifo_size(ovl->id); |
| 14624 | + if (use_fifomerge) |
| 14625 | + size *= 3; |
| 14626 | + |
| 14627 | + switch (dssdev->type) { |
| 14628 | + case OMAP_DISPLAY_TYPE_DPI: |
| 14629 | + case OMAP_DISPLAY_TYPE_DBI: |
| 14630 | + case OMAP_DISPLAY_TYPE_SDI: |
| 14631 | + case OMAP_DISPLAY_TYPE_VENC: |
| 14632 | + default_get_overlay_fifo_thresholds(ovl->id, size, |
| 14633 | + &oc->burst_size, &oc->fifo_low, |
| 14634 | + &oc->fifo_high); |
| 14635 | + break; |
| 14636 | +#ifdef CONFIG_OMAP2_DSS_DSI |
| 14637 | + case OMAP_DISPLAY_TYPE_DSI: |
| 14638 | + dsi_get_overlay_fifo_thresholds(ovl->id, size, |
| 14639 | + &oc->burst_size, &oc->fifo_low, |
| 14640 | + &oc->fifo_high); |
| 14641 | + break; |
| 14642 | +#endif |
| 14643 | + default: |
| 14644 | + BUG(); |
| 14645 | + } |
| 14646 | + } |
| 14647 | + |
| 14648 | + r = 0; |
| 14649 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 14650 | + if (!dss_cache.irq_enabled) { |
| 14651 | + r = omap_dispc_register_isr(dss_apply_irq_handler, NULL, |
| 14652 | + DISPC_IRQ_VSYNC | DISPC_IRQ_EVSYNC_ODD | |
| 14653 | + DISPC_IRQ_EVSYNC_EVEN); |
| 14654 | + dss_cache.irq_enabled = true; |
| 14655 | + } |
| 14656 | + configure_dispc(); |
| 14657 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 14658 | + |
| 14659 | + spin_unlock_irqrestore(&dss_cache.lock, flags); |
| 14660 | + |
| 14661 | + return r; |
| 14662 | +} |
| 14663 | + |
| 14664 | +static int dss_check_manager(struct omap_overlay_manager *mgr) |
| 14665 | +{ |
| 14666 | + /* OMAP supports only graphics source transparency color key and alpha |
| 14667 | + * blending simultaneously. See TRM 15.4.2.4.2.2 Alpha Mode */ |
| 14668 | + |
| 14669 | + if (mgr->info.alpha_enabled && mgr->info.trans_enabled && |
| 14670 | + mgr->info.trans_key_type != OMAP_DSS_COLOR_KEY_GFX_DST) |
| 14671 | + return -EINVAL; |
| 14672 | + |
| 14673 | + return 0; |
| 14674 | +} |
| 14675 | + |
| 14676 | +static int omap_dss_mgr_set_info(struct omap_overlay_manager *mgr, |
| 14677 | + struct omap_overlay_manager_info *info) |
| 14678 | +{ |
| 14679 | + int r; |
| 14680 | + struct omap_overlay_manager_info old_info; |
| 14681 | + |
| 14682 | + old_info = mgr->info; |
| 14683 | + mgr->info = *info; |
| 14684 | + |
| 14685 | + r = dss_check_manager(mgr); |
| 14686 | + if (r) { |
| 14687 | + mgr->info = old_info; |
| 14688 | + return r; |
| 14689 | + } |
| 14690 | + |
| 14691 | + mgr->info_dirty = true; |
| 14692 | + |
| 14693 | + return 0; |
| 14694 | +} |
| 14695 | + |
| 14696 | +static void omap_dss_mgr_get_info(struct omap_overlay_manager *mgr, |
| 14697 | + struct omap_overlay_manager_info *info) |
| 14698 | +{ |
| 14699 | + *info = mgr->info; |
| 14700 | +} |
| 14701 | + |
| 14702 | +static void omap_dss_add_overlay_manager(struct omap_overlay_manager *manager) |
| 14703 | +{ |
| 14704 | + ++num_managers; |
| 14705 | + list_add_tail(&manager->list, &manager_list); |
| 14706 | +} |
| 14707 | + |
| 14708 | +int dss_init_overlay_managers(struct platform_device *pdev) |
| 14709 | +{ |
| 14710 | + int i, r; |
| 14711 | + |
| 14712 | + spin_lock_init(&dss_cache.lock); |
| 14713 | + |
| 14714 | + INIT_LIST_HEAD(&manager_list); |
| 14715 | + |
| 14716 | + num_managers = 0; |
| 14717 | + |
| 14718 | + for (i = 0; i < 2; ++i) { |
| 14719 | + struct omap_overlay_manager *mgr; |
| 14720 | + mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); |
| 14721 | + |
| 14722 | + BUG_ON(mgr == NULL); |
| 14723 | + |
| 14724 | + switch (i) { |
| 14725 | + case 0: |
| 14726 | + mgr->name = "lcd"; |
| 14727 | + mgr->id = OMAP_DSS_CHANNEL_LCD; |
| 14728 | + mgr->supported_displays = |
| 14729 | + OMAP_DISPLAY_TYPE_DPI | OMAP_DISPLAY_TYPE_DBI | |
| 14730 | + OMAP_DISPLAY_TYPE_SDI | OMAP_DISPLAY_TYPE_DSI; |
| 14731 | + break; |
| 14732 | + case 1: |
| 14733 | + mgr->name = "tv"; |
| 14734 | + mgr->id = OMAP_DSS_CHANNEL_DIGIT; |
| 14735 | + mgr->supported_displays = OMAP_DISPLAY_TYPE_VENC; |
| 14736 | + break; |
| 14737 | + } |
| 14738 | + |
| 14739 | + mgr->set_device = &omap_dss_set_device; |
| 14740 | + mgr->unset_device = &omap_dss_unset_device; |
| 14741 | + mgr->apply = &omap_dss_mgr_apply; |
| 14742 | + mgr->set_manager_info = &omap_dss_mgr_set_info; |
| 14743 | + mgr->get_manager_info = &omap_dss_mgr_get_info; |
| 14744 | + mgr->wait_for_go = &dss_mgr_wait_for_go; |
| 14745 | + |
| 14746 | + mgr->caps = OMAP_DSS_OVL_MGR_CAP_DISPC; |
| 14747 | + |
| 14748 | + dss_overlay_setup_dispc_manager(mgr); |
| 14749 | + |
| 14750 | + omap_dss_add_overlay_manager(mgr); |
| 14751 | + |
| 14752 | + r = kobject_init_and_add(&mgr->kobj, &manager_ktype, |
| 14753 | + &pdev->dev.kobj, "manager%d", i); |
| 14754 | + |
| 14755 | + if (r) { |
| 14756 | + DSSERR("failed to create sysfs file\n"); |
| 14757 | + continue; |
| 14758 | + } |
| 14759 | + } |
| 14760 | + |
| 14761 | +#ifdef L4_EXAMPLE |
| 14762 | + { |
| 14763 | + int omap_dss_mgr_apply_l4(struct omap_overlay_manager *mgr) |
| 14764 | + { |
| 14765 | + DSSDBG("omap_dss_mgr_apply_l4(%s)\n", mgr->name); |
| 14766 | + |
| 14767 | + return 0; |
| 14768 | + } |
| 14769 | + |
| 14770 | + struct omap_overlay_manager *mgr; |
| 14771 | + mgr = kzalloc(sizeof(*mgr), GFP_KERNEL); |
| 14772 | + |
| 14773 | + BUG_ON(mgr == NULL); |
| 14774 | + |
| 14775 | + mgr->name = "l4"; |
| 14776 | + mgr->supported_displays = |
| 14777 | + OMAP_DISPLAY_TYPE_DBI | OMAP_DISPLAY_TYPE_DSI; |
| 14778 | + |
| 14779 | + mgr->set_device = &omap_dss_set_device; |
| 14780 | + mgr->unset_device = &omap_dss_unset_device; |
| 14781 | + mgr->apply = &omap_dss_mgr_apply_l4; |
| 14782 | + mgr->set_manager_info = &omap_dss_mgr_set_info; |
| 14783 | + mgr->get_manager_info = &omap_dss_mgr_get_info; |
| 14784 | + |
| 14785 | + dss_overlay_setup_l4_manager(mgr); |
| 14786 | + |
| 14787 | + omap_dss_add_overlay_manager(mgr); |
| 14788 | + |
| 14789 | + r = kobject_init_and_add(&mgr->kobj, &manager_ktype, |
| 14790 | + &pdev->dev.kobj, "managerl4"); |
| 14791 | + |
| 14792 | + if (r) |
| 14793 | + DSSERR("failed to create sysfs file\n"); |
| 14794 | + } |
| 14795 | +#endif |
| 14796 | + |
| 14797 | + return 0; |
| 14798 | +} |
| 14799 | + |
| 14800 | +void dss_uninit_overlay_managers(struct platform_device *pdev) |
| 14801 | +{ |
| 14802 | + struct omap_overlay_manager *mgr; |
| 14803 | + |
| 14804 | + while (!list_empty(&manager_list)) { |
| 14805 | + mgr = list_first_entry(&manager_list, |
| 14806 | + struct omap_overlay_manager, list); |
| 14807 | + list_del(&mgr->list); |
| 14808 | + kobject_del(&mgr->kobj); |
| 14809 | + kobject_put(&mgr->kobj); |
| 14810 | + kfree(mgr); |
| 14811 | + } |
| 14812 | + |
| 14813 | + num_managers = 0; |
| 14814 | +} |
| 14815 | + |
| 14816 | +int omap_dss_get_num_overlay_managers(void) |
| 14817 | +{ |
| 14818 | + return num_managers; |
| 14819 | +} |
| 14820 | +EXPORT_SYMBOL(omap_dss_get_num_overlay_managers); |
| 14821 | + |
| 14822 | +struct omap_overlay_manager *omap_dss_get_overlay_manager(int num) |
| 14823 | +{ |
| 14824 | + int i = 0; |
| 14825 | + struct omap_overlay_manager *mgr; |
| 14826 | + |
| 14827 | + list_for_each_entry(mgr, &manager_list, list) { |
| 14828 | + if (i++ == num) |
| 14829 | + return mgr; |
| 14830 | + } |
| 14831 | + |
| 14832 | + return NULL; |
| 14833 | +} |
| 14834 | +EXPORT_SYMBOL(omap_dss_get_overlay_manager); |
| 14835 | + |
| 14836 | --- /dev/null |
| 14837 | +++ b/drivers/video/omap2/dss/overlay.c |
| 14838 | @@ -0,0 +1,673 @@ |
| 14839 | +/* |
| 14840 | + * linux/drivers/video/omap2/dss/overlay.c |
| 14841 | + * |
| 14842 | + * Copyright (C) 2009 Nokia Corporation |
| 14843 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 14844 | + * |
| 14845 | + * Some code and ideas taken from drivers/video/omap/ driver |
| 14846 | + * by Imre Deak. |
| 14847 | + * |
| 14848 | + * This program is free software; you can redistribute it and/or modify it |
| 14849 | + * under the terms of the GNU General Public License version 2 as published by |
| 14850 | + * the Free Software Foundation. |
| 14851 | + * |
| 14852 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 14853 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 14854 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 14855 | + * more details. |
| 14856 | + * |
| 14857 | + * You should have received a copy of the GNU General Public License along with |
| 14858 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 14859 | + */ |
| 14860 | + |
| 14861 | +#define DSS_SUBSYS_NAME "OVERLAY" |
| 14862 | + |
| 14863 | +#include <linux/kernel.h> |
| 14864 | +#include <linux/module.h> |
| 14865 | +#include <linux/err.h> |
| 14866 | +#include <linux/sysfs.h> |
| 14867 | +#include <linux/kobject.h> |
| 14868 | +#include <linux/platform_device.h> |
| 14869 | +#include <linux/delay.h> |
| 14870 | + |
| 14871 | +#include <mach/display.h> |
| 14872 | + |
| 14873 | +#include "dss.h" |
| 14874 | + |
| 14875 | +static int num_overlays; |
| 14876 | +static struct list_head overlay_list; |
| 14877 | + |
| 14878 | +static ssize_t overlay_name_show(struct omap_overlay *ovl, char *buf) |
| 14879 | +{ |
| 14880 | + return snprintf(buf, PAGE_SIZE, "%s\n", ovl->name); |
| 14881 | +} |
| 14882 | + |
| 14883 | +static ssize_t overlay_manager_show(struct omap_overlay *ovl, char *buf) |
| 14884 | +{ |
| 14885 | + return snprintf(buf, PAGE_SIZE, "%s\n", |
| 14886 | + ovl->manager ? ovl->manager->name : "<none>"); |
| 14887 | +} |
| 14888 | + |
| 14889 | +static ssize_t overlay_manager_store(struct omap_overlay *ovl, const char *buf, |
| 14890 | + size_t size) |
| 14891 | +{ |
| 14892 | + int i, r; |
| 14893 | + struct omap_overlay_manager *mgr = NULL; |
| 14894 | + struct omap_overlay_manager *old_mgr; |
| 14895 | + int len = size; |
| 14896 | + |
| 14897 | + if (buf[size-1] == '\n') |
| 14898 | + --len; |
| 14899 | + |
| 14900 | + if (len > 0) { |
| 14901 | + for (i = 0; i < omap_dss_get_num_overlay_managers(); ++i) { |
| 14902 | + mgr = omap_dss_get_overlay_manager(i); |
| 14903 | + |
| 14904 | + if (strncmp(buf, mgr->name, len) == 0) |
| 14905 | + break; |
| 14906 | + |
| 14907 | + mgr = NULL; |
| 14908 | + } |
| 14909 | + } |
| 14910 | + |
| 14911 | + if (len > 0 && mgr == NULL) |
| 14912 | + return -EINVAL; |
| 14913 | + |
| 14914 | + if (mgr) |
| 14915 | + DSSDBG("manager %s found\n", mgr->name); |
| 14916 | + |
| 14917 | + if (mgr == ovl->manager) |
| 14918 | + return size; |
| 14919 | + |
| 14920 | + old_mgr = ovl->manager; |
| 14921 | + |
| 14922 | + /* detach old manager */ |
| 14923 | + if (old_mgr) { |
| 14924 | + r = ovl->unset_manager(ovl); |
| 14925 | + if (r) { |
| 14926 | + DSSERR("detach failed\n"); |
| 14927 | + return r; |
| 14928 | + } |
| 14929 | + |
| 14930 | + r = old_mgr->apply(old_mgr); |
| 14931 | + if (r) |
| 14932 | + return r; |
| 14933 | + } |
| 14934 | + |
| 14935 | + if (mgr) { |
| 14936 | + r = ovl->set_manager(ovl, mgr); |
| 14937 | + if (r) { |
| 14938 | + DSSERR("Failed to attach overlay\n"); |
| 14939 | + return r; |
| 14940 | + } |
| 14941 | + |
| 14942 | + r = mgr->apply(mgr); |
| 14943 | + if (r) |
| 14944 | + return r; |
| 14945 | + } |
| 14946 | + |
| 14947 | + return size; |
| 14948 | +} |
| 14949 | + |
| 14950 | +static ssize_t overlay_input_size_show(struct omap_overlay *ovl, char *buf) |
| 14951 | +{ |
| 14952 | + return snprintf(buf, PAGE_SIZE, "%d,%d\n", |
| 14953 | + ovl->info.width, ovl->info.height); |
| 14954 | +} |
| 14955 | + |
| 14956 | +static ssize_t overlay_screen_width_show(struct omap_overlay *ovl, char *buf) |
| 14957 | +{ |
| 14958 | + return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.screen_width); |
| 14959 | +} |
| 14960 | + |
| 14961 | +static ssize_t overlay_position_show(struct omap_overlay *ovl, char *buf) |
| 14962 | +{ |
| 14963 | + return snprintf(buf, PAGE_SIZE, "%d,%d\n", |
| 14964 | + ovl->info.pos_x, ovl->info.pos_y); |
| 14965 | +} |
| 14966 | + |
| 14967 | +static ssize_t overlay_position_store(struct omap_overlay *ovl, |
| 14968 | + const char *buf, size_t size) |
| 14969 | +{ |
| 14970 | + int r; |
| 14971 | + char *last; |
| 14972 | + struct omap_overlay_info info; |
| 14973 | + |
| 14974 | + ovl->get_overlay_info(ovl, &info); |
| 14975 | + |
| 14976 | + info.pos_x = simple_strtoul(buf, &last, 10); |
| 14977 | + ++last; |
| 14978 | + if (last - buf >= size) |
| 14979 | + return -EINVAL; |
| 14980 | + |
| 14981 | + info.pos_y = simple_strtoul(last, &last, 10); |
| 14982 | + |
| 14983 | + r = ovl->set_overlay_info(ovl, &info); |
| 14984 | + if (r) |
| 14985 | + return r; |
| 14986 | + |
| 14987 | + if (ovl->manager) { |
| 14988 | + r = ovl->manager->apply(ovl->manager); |
| 14989 | + if (r) |
| 14990 | + return r; |
| 14991 | + } |
| 14992 | + |
| 14993 | + return size; |
| 14994 | +} |
| 14995 | + |
| 14996 | +static ssize_t overlay_output_size_show(struct omap_overlay *ovl, char *buf) |
| 14997 | +{ |
| 14998 | + return snprintf(buf, PAGE_SIZE, "%d,%d\n", |
| 14999 | + ovl->info.out_width, ovl->info.out_height); |
| 15000 | +} |
| 15001 | + |
| 15002 | +static ssize_t overlay_output_size_store(struct omap_overlay *ovl, |
| 15003 | + const char *buf, size_t size) |
| 15004 | +{ |
| 15005 | + int r; |
| 15006 | + char *last; |
| 15007 | + struct omap_overlay_info info; |
| 15008 | + |
| 15009 | + ovl->get_overlay_info(ovl, &info); |
| 15010 | + |
| 15011 | + info.out_width = simple_strtoul(buf, &last, 10); |
| 15012 | + ++last; |
| 15013 | + if (last - buf >= size) |
| 15014 | + return -EINVAL; |
| 15015 | + |
| 15016 | + info.out_height = simple_strtoul(last, &last, 10); |
| 15017 | + |
| 15018 | + r = ovl->set_overlay_info(ovl, &info); |
| 15019 | + if (r) |
| 15020 | + return r; |
| 15021 | + |
| 15022 | + if (ovl->manager) { |
| 15023 | + r = ovl->manager->apply(ovl->manager); |
| 15024 | + if (r) |
| 15025 | + return r; |
| 15026 | + } |
| 15027 | + |
| 15028 | + return size; |
| 15029 | +} |
| 15030 | + |
| 15031 | +static ssize_t overlay_enabled_show(struct omap_overlay *ovl, char *buf) |
| 15032 | +{ |
| 15033 | + return snprintf(buf, PAGE_SIZE, "%d\n", ovl->info.enabled); |
| 15034 | +} |
| 15035 | + |
| 15036 | +static ssize_t overlay_enabled_store(struct omap_overlay *ovl, const char *buf, |
| 15037 | + size_t size) |
| 15038 | +{ |
| 15039 | + int r; |
| 15040 | + struct omap_overlay_info info; |
| 15041 | + |
| 15042 | + ovl->get_overlay_info(ovl, &info); |
| 15043 | + |
| 15044 | + info.enabled = simple_strtoul(buf, NULL, 10); |
| 15045 | + |
| 15046 | + r = ovl->set_overlay_info(ovl, &info); |
| 15047 | + if (r) |
| 15048 | + return r; |
| 15049 | + |
| 15050 | + if (ovl->manager) { |
| 15051 | + r = ovl->manager->apply(ovl->manager); |
| 15052 | + if (r) |
| 15053 | + return r; |
| 15054 | + } |
| 15055 | + |
| 15056 | + return size; |
| 15057 | +} |
| 15058 | + |
| 15059 | +static ssize_t overlay_global_alpha_show(struct omap_overlay *ovl, char *buf) |
| 15060 | +{ |
| 15061 | + return snprintf(buf, PAGE_SIZE, "%d\n", |
| 15062 | + ovl->info.global_alpha); |
| 15063 | +} |
| 15064 | + |
| 15065 | +static ssize_t overlay_global_alpha_store(struct omap_overlay *ovl, |
| 15066 | + const char *buf, size_t size) |
| 15067 | +{ |
| 15068 | + int r; |
| 15069 | + struct omap_overlay_info info; |
| 15070 | + |
| 15071 | + ovl->get_overlay_info(ovl, &info); |
| 15072 | + |
| 15073 | + /* Video1 plane does not support global alpha |
| 15074 | + * to always make it 255 completely opaque |
| 15075 | + */ |
| 15076 | + if (ovl->id == OMAP_DSS_VIDEO1) |
| 15077 | + info.global_alpha = 255; |
| 15078 | + else |
| 15079 | + info.global_alpha = simple_strtoul(buf, NULL, 10); |
| 15080 | + |
| 15081 | + r = ovl->set_overlay_info(ovl, &info); |
| 15082 | + if (r) |
| 15083 | + return r; |
| 15084 | + |
| 15085 | + if (ovl->manager) { |
| 15086 | + r = ovl->manager->apply(ovl->manager); |
| 15087 | + if (r) |
| 15088 | + return r; |
| 15089 | + } |
| 15090 | + |
| 15091 | + return size; |
| 15092 | +} |
| 15093 | + |
| 15094 | +struct overlay_attribute { |
| 15095 | + struct attribute attr; |
| 15096 | + ssize_t (*show)(struct omap_overlay *, char *); |
| 15097 | + ssize_t (*store)(struct omap_overlay *, const char *, size_t); |
| 15098 | +}; |
| 15099 | + |
| 15100 | +#define OVERLAY_ATTR(_name, _mode, _show, _store) \ |
| 15101 | + struct overlay_attribute overlay_attr_##_name = \ |
| 15102 | + __ATTR(_name, _mode, _show, _store) |
| 15103 | + |
| 15104 | +static OVERLAY_ATTR(name, S_IRUGO, overlay_name_show, NULL); |
| 15105 | +static OVERLAY_ATTR(manager, S_IRUGO|S_IWUSR, |
| 15106 | + overlay_manager_show, overlay_manager_store); |
| 15107 | +static OVERLAY_ATTR(input_size, S_IRUGO, overlay_input_size_show, NULL); |
| 15108 | +static OVERLAY_ATTR(screen_width, S_IRUGO, overlay_screen_width_show, NULL); |
| 15109 | +static OVERLAY_ATTR(position, S_IRUGO|S_IWUSR, |
| 15110 | + overlay_position_show, overlay_position_store); |
| 15111 | +static OVERLAY_ATTR(output_size, S_IRUGO|S_IWUSR, |
| 15112 | + overlay_output_size_show, overlay_output_size_store); |
| 15113 | +static OVERLAY_ATTR(enabled, S_IRUGO|S_IWUSR, |
| 15114 | + overlay_enabled_show, overlay_enabled_store); |
| 15115 | +static OVERLAY_ATTR(global_alpha, S_IRUGO|S_IWUSR, |
| 15116 | + overlay_global_alpha_show, overlay_global_alpha_store); |
| 15117 | + |
| 15118 | +static struct attribute *overlay_sysfs_attrs[] = { |
| 15119 | + &overlay_attr_name.attr, |
| 15120 | + &overlay_attr_manager.attr, |
| 15121 | + &overlay_attr_input_size.attr, |
| 15122 | + &overlay_attr_screen_width.attr, |
| 15123 | + &overlay_attr_position.attr, |
| 15124 | + &overlay_attr_output_size.attr, |
| 15125 | + &overlay_attr_enabled.attr, |
| 15126 | + &overlay_attr_global_alpha.attr, |
| 15127 | + NULL |
| 15128 | +}; |
| 15129 | + |
| 15130 | +static ssize_t overlay_attr_show(struct kobject *kobj, struct attribute *attr, |
| 15131 | + char *buf) |
| 15132 | +{ |
| 15133 | + struct omap_overlay *overlay; |
| 15134 | + struct overlay_attribute *overlay_attr; |
| 15135 | + |
| 15136 | + overlay = container_of(kobj, struct omap_overlay, kobj); |
| 15137 | + overlay_attr = container_of(attr, struct overlay_attribute, attr); |
| 15138 | + |
| 15139 | + if (!overlay_attr->show) |
| 15140 | + return -ENOENT; |
| 15141 | + |
| 15142 | + return overlay_attr->show(overlay, buf); |
| 15143 | +} |
| 15144 | + |
| 15145 | +static ssize_t overlay_attr_store(struct kobject *kobj, struct attribute *attr, |
| 15146 | + const char *buf, size_t size) |
| 15147 | +{ |
| 15148 | + struct omap_overlay *overlay; |
| 15149 | + struct overlay_attribute *overlay_attr; |
| 15150 | + |
| 15151 | + overlay = container_of(kobj, struct omap_overlay, kobj); |
| 15152 | + overlay_attr = container_of(attr, struct overlay_attribute, attr); |
| 15153 | + |
| 15154 | + if (!overlay_attr->store) |
| 15155 | + return -ENOENT; |
| 15156 | + |
| 15157 | + return overlay_attr->store(overlay, buf, size); |
| 15158 | +} |
| 15159 | + |
| 15160 | +static struct sysfs_ops overlay_sysfs_ops = { |
| 15161 | + .show = overlay_attr_show, |
| 15162 | + .store = overlay_attr_store, |
| 15163 | +}; |
| 15164 | + |
| 15165 | +static struct kobj_type overlay_ktype = { |
| 15166 | + .sysfs_ops = &overlay_sysfs_ops, |
| 15167 | + .default_attrs = overlay_sysfs_attrs, |
| 15168 | +}; |
| 15169 | + |
| 15170 | +/* Check if overlay parameters are compatible with display */ |
| 15171 | +int dss_check_overlay(struct omap_overlay *ovl, struct omap_dss_device *dssdev) |
| 15172 | +{ |
| 15173 | + struct omap_overlay_info *info; |
| 15174 | + u16 outw, outh; |
| 15175 | + u16 dw, dh; |
| 15176 | + |
| 15177 | + if (!dssdev) |
| 15178 | + return 0; |
| 15179 | + |
| 15180 | + if (!ovl->info.enabled) |
| 15181 | + return 0; |
| 15182 | + |
| 15183 | + info = &ovl->info; |
| 15184 | + |
| 15185 | + if (info->paddr == 0) { |
| 15186 | + DSSDBG("check_overlay failed: paddr 0\n"); |
| 15187 | + return -EINVAL; |
| 15188 | + } |
| 15189 | + |
| 15190 | + dssdev->get_resolution(dssdev, &dw, &dh); |
| 15191 | + |
| 15192 | + DSSDBG("check_overlay %d: (%d,%d %dx%d -> %dx%d) disp (%dx%d)\n", |
| 15193 | + ovl->id, |
| 15194 | + info->pos_x, info->pos_y, |
| 15195 | + info->width, info->height, |
| 15196 | + info->out_width, info->out_height, |
| 15197 | + dw, dh); |
| 15198 | + |
| 15199 | + if ((ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { |
| 15200 | + outw = info->width; |
| 15201 | + outh = info->height; |
| 15202 | + } else { |
| 15203 | + if (info->out_width == 0) |
| 15204 | + outw = info->width; |
| 15205 | + else |
| 15206 | + outw = info->out_width; |
| 15207 | + |
| 15208 | + if (info->out_height == 0) |
| 15209 | + outh = info->height; |
| 15210 | + else |
| 15211 | + outh = info->out_height; |
| 15212 | + } |
| 15213 | + |
| 15214 | + if (dw < info->pos_x + outw) { |
| 15215 | + DSSDBG("check_overlay failed 1: %d < %d + %d\n", |
| 15216 | + dw, info->pos_x, outw); |
| 15217 | + return -EINVAL; |
| 15218 | + } |
| 15219 | + |
| 15220 | + if (dh < info->pos_y + outh) { |
| 15221 | + DSSDBG("check_overlay failed 2: %d < %d + %d\n", |
| 15222 | + dh, info->pos_y, outh); |
| 15223 | + return -EINVAL; |
| 15224 | + } |
| 15225 | + |
| 15226 | + if ((ovl->supported_modes & info->color_mode) == 0) { |
| 15227 | + DSSERR("overlay doesn't support mode %d\n", info->color_mode); |
| 15228 | + return -EINVAL; |
| 15229 | + } |
| 15230 | + |
| 15231 | + return 0; |
| 15232 | +} |
| 15233 | + |
| 15234 | +static int dss_ovl_set_overlay_info(struct omap_overlay *ovl, |
| 15235 | + struct omap_overlay_info *info) |
| 15236 | +{ |
| 15237 | + int r; |
| 15238 | + struct omap_overlay_info old_info; |
| 15239 | + |
| 15240 | + old_info = ovl->info; |
| 15241 | + ovl->info = *info; |
| 15242 | + |
| 15243 | + if (ovl->manager) { |
| 15244 | + r = dss_check_overlay(ovl, ovl->manager->device); |
| 15245 | + if (r) { |
| 15246 | + ovl->info = old_info; |
| 15247 | + return r; |
| 15248 | + } |
| 15249 | + } |
| 15250 | + |
| 15251 | + ovl->info_dirty = true; |
| 15252 | + |
| 15253 | + return 0; |
| 15254 | +} |
| 15255 | + |
| 15256 | +static void dss_ovl_get_overlay_info(struct omap_overlay *ovl, |
| 15257 | + struct omap_overlay_info *info) |
| 15258 | +{ |
| 15259 | + *info = ovl->info; |
| 15260 | +} |
| 15261 | + |
| 15262 | +static int dss_ovl_wait_for_go(struct omap_overlay *ovl) |
| 15263 | +{ |
| 15264 | + return dss_mgr_wait_for_go_ovl(ovl); |
| 15265 | +} |
| 15266 | + |
| 15267 | +static int omap_dss_set_manager(struct omap_overlay *ovl, |
| 15268 | + struct omap_overlay_manager *mgr) |
| 15269 | +{ |
| 15270 | + if (!mgr) |
| 15271 | + return -EINVAL; |
| 15272 | + |
| 15273 | + if (ovl->manager) { |
| 15274 | + DSSERR("overlay '%s' already has a manager '%s'\n", |
| 15275 | + ovl->name, ovl->manager->name); |
| 15276 | + return -EINVAL; |
| 15277 | + } |
| 15278 | + |
| 15279 | + if (ovl->info.enabled) { |
| 15280 | + DSSERR("overlay has to be disabled to change the manager\n"); |
| 15281 | + return -EINVAL; |
| 15282 | + } |
| 15283 | + |
| 15284 | + ovl->manager = mgr; |
| 15285 | + |
| 15286 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 15287 | + /* XXX: on manual update display, in auto update mode, a bug happens |
| 15288 | + * here. When an overlay is first enabled on LCD, then it's disabled, |
| 15289 | + * and the manager is changed to TV, we sometimes get SYNC_LOST_DIGIT |
| 15290 | + * errors. Waiting before changing the channel_out fixes it. I'm |
| 15291 | + * guessing that the overlay is still somehow being used for the LCD, |
| 15292 | + * but I don't understand how or why. */ |
| 15293 | + msleep(40); |
| 15294 | + dispc_set_channel_out(ovl->id, mgr->id); |
| 15295 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 15296 | + |
| 15297 | + return 0; |
| 15298 | +} |
| 15299 | + |
| 15300 | +static int omap_dss_unset_manager(struct omap_overlay *ovl) |
| 15301 | +{ |
| 15302 | + int r; |
| 15303 | + |
| 15304 | + if (!ovl->manager) { |
| 15305 | + DSSERR("failed to detach overlay: manager not set\n"); |
| 15306 | + return -EINVAL; |
| 15307 | + } |
| 15308 | + |
| 15309 | + if (ovl->info.enabled) { |
| 15310 | + DSSERR("overlay has to be disabled to unset the manager\n"); |
| 15311 | + return -EINVAL; |
| 15312 | + } |
| 15313 | + |
| 15314 | + r = ovl->wait_for_go(ovl); |
| 15315 | + if (r) |
| 15316 | + return r; |
| 15317 | + |
| 15318 | + ovl->manager = NULL; |
| 15319 | + |
| 15320 | + return 0; |
| 15321 | +} |
| 15322 | + |
| 15323 | +int omap_dss_get_num_overlays(void) |
| 15324 | +{ |
| 15325 | + return num_overlays; |
| 15326 | +} |
| 15327 | +EXPORT_SYMBOL(omap_dss_get_num_overlays); |
| 15328 | + |
| 15329 | +struct omap_overlay *omap_dss_get_overlay(int num) |
| 15330 | +{ |
| 15331 | + int i = 0; |
| 15332 | + struct omap_overlay *ovl; |
| 15333 | + |
| 15334 | + list_for_each_entry(ovl, &overlay_list, list) { |
| 15335 | + if (i++ == num) |
| 15336 | + return ovl; |
| 15337 | + } |
| 15338 | + |
| 15339 | + return NULL; |
| 15340 | +} |
| 15341 | +EXPORT_SYMBOL(omap_dss_get_overlay); |
| 15342 | + |
| 15343 | +static void omap_dss_add_overlay(struct omap_overlay *overlay) |
| 15344 | +{ |
| 15345 | + ++num_overlays; |
| 15346 | + list_add_tail(&overlay->list, &overlay_list); |
| 15347 | +} |
| 15348 | + |
| 15349 | +static struct omap_overlay *dispc_overlays[3]; |
| 15350 | + |
| 15351 | +void dss_overlay_setup_dispc_manager(struct omap_overlay_manager *mgr) |
| 15352 | +{ |
| 15353 | + mgr->num_overlays = 3; |
| 15354 | + mgr->overlays = dispc_overlays; |
| 15355 | +} |
| 15356 | + |
| 15357 | +#ifdef L4_EXAMPLE |
| 15358 | +static struct omap_overlay *l4_overlays[1]; |
| 15359 | +void dss_overlay_setup_l4_manager(struct omap_overlay_manager *mgr) |
| 15360 | +{ |
| 15361 | + mgr->num_overlays = 1; |
| 15362 | + mgr->overlays = l4_overlays; |
| 15363 | +} |
| 15364 | +#endif |
| 15365 | + |
| 15366 | +void dss_init_overlays(struct platform_device *pdev) |
| 15367 | +{ |
| 15368 | + int i, r; |
| 15369 | + |
| 15370 | + INIT_LIST_HEAD(&overlay_list); |
| 15371 | + |
| 15372 | + num_overlays = 0; |
| 15373 | + |
| 15374 | + for (i = 0; i < 3; ++i) { |
| 15375 | + struct omap_overlay *ovl; |
| 15376 | + ovl = kzalloc(sizeof(*ovl), GFP_KERNEL); |
| 15377 | + |
| 15378 | + BUG_ON(ovl == NULL); |
| 15379 | + |
| 15380 | + switch (i) { |
| 15381 | + case 0: |
| 15382 | + ovl->name = "gfx"; |
| 15383 | + ovl->id = OMAP_DSS_GFX; |
| 15384 | + ovl->supported_modes = OMAP_DSS_COLOR_GFX_OMAP3; |
| 15385 | + ovl->caps = OMAP_DSS_OVL_CAP_DISPC; |
| 15386 | + ovl->info.global_alpha = 255; |
| 15387 | + break; |
| 15388 | + case 1: |
| 15389 | + ovl->name = "vid1"; |
| 15390 | + ovl->id = OMAP_DSS_VIDEO1; |
| 15391 | + ovl->supported_modes = OMAP_DSS_COLOR_VID_OMAP3; |
| 15392 | + ovl->caps = OMAP_DSS_OVL_CAP_SCALE | |
| 15393 | + OMAP_DSS_OVL_CAP_DISPC; |
| 15394 | + ovl->info.global_alpha = 255; |
| 15395 | + break; |
| 15396 | + case 2: |
| 15397 | + ovl->name = "vid2"; |
| 15398 | + ovl->id = OMAP_DSS_VIDEO2; |
| 15399 | + ovl->supported_modes = OMAP_DSS_COLOR_VID_OMAP3; |
| 15400 | + ovl->caps = OMAP_DSS_OVL_CAP_SCALE | |
| 15401 | + OMAP_DSS_OVL_CAP_DISPC; |
| 15402 | + ovl->info.global_alpha = 255; |
| 15403 | + break; |
| 15404 | + } |
| 15405 | + |
| 15406 | + ovl->set_manager = &omap_dss_set_manager; |
| 15407 | + ovl->unset_manager = &omap_dss_unset_manager; |
| 15408 | + ovl->set_overlay_info = &dss_ovl_set_overlay_info; |
| 15409 | + ovl->get_overlay_info = &dss_ovl_get_overlay_info; |
| 15410 | + ovl->wait_for_go = &dss_ovl_wait_for_go; |
| 15411 | + |
| 15412 | + omap_dss_add_overlay(ovl); |
| 15413 | + |
| 15414 | + r = kobject_init_and_add(&ovl->kobj, &overlay_ktype, |
| 15415 | + &pdev->dev.kobj, "overlay%d", i); |
| 15416 | + |
| 15417 | + if (r) { |
| 15418 | + DSSERR("failed to create sysfs file\n"); |
| 15419 | + continue; |
| 15420 | + } |
| 15421 | + |
| 15422 | + dispc_overlays[i] = ovl; |
| 15423 | + } |
| 15424 | + |
| 15425 | +#ifdef L4_EXAMPLE |
| 15426 | + { |
| 15427 | + struct omap_overlay *ovl; |
| 15428 | + ovl = kzalloc(sizeof(*ovl), GFP_KERNEL); |
| 15429 | + |
| 15430 | + BUG_ON(ovl == NULL); |
| 15431 | + |
| 15432 | + ovl->name = "l4"; |
| 15433 | + ovl->supported_modes = OMAP_DSS_COLOR_RGB24U; |
| 15434 | + |
| 15435 | + ovl->set_manager = &omap_dss_set_manager; |
| 15436 | + ovl->unset_manager = &omap_dss_unset_manager; |
| 15437 | + ovl->set_overlay_info = &dss_ovl_set_overlay_info; |
| 15438 | + ovl->get_overlay_info = &dss_ovl_get_overlay_info; |
| 15439 | + |
| 15440 | + omap_dss_add_overlay(ovl); |
| 15441 | + |
| 15442 | + r = kobject_init_and_add(&ovl->kobj, &overlay_ktype, |
| 15443 | + &pdev->dev.kobj, "overlayl4"); |
| 15444 | + |
| 15445 | + if (r) |
| 15446 | + DSSERR("failed to create sysfs file\n"); |
| 15447 | + |
| 15448 | + l4_overlays[0] = ovl; |
| 15449 | + } |
| 15450 | +#endif |
| 15451 | +} |
| 15452 | + |
| 15453 | +/* connect overlays to the new device, if not already connected. if force |
| 15454 | + * selected, connect always. */ |
| 15455 | +void dss_recheck_connections(struct omap_dss_device *dssdev, bool force) |
| 15456 | +{ |
| 15457 | + int i; |
| 15458 | + struct omap_overlay_manager *lcd_mgr; |
| 15459 | + struct omap_overlay_manager *tv_mgr; |
| 15460 | + struct omap_overlay_manager *mgr = NULL; |
| 15461 | + |
| 15462 | + lcd_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_LCD); |
| 15463 | + tv_mgr = omap_dss_get_overlay_manager(OMAP_DSS_OVL_MGR_TV); |
| 15464 | + |
| 15465 | + if (dssdev->type != OMAP_DISPLAY_TYPE_VENC) { |
| 15466 | + if (!lcd_mgr->device || force) { |
| 15467 | + if (lcd_mgr->device) |
| 15468 | + lcd_mgr->unset_device(lcd_mgr); |
| 15469 | + lcd_mgr->set_device(lcd_mgr, dssdev); |
| 15470 | + mgr = lcd_mgr; |
| 15471 | + } |
| 15472 | + } |
| 15473 | + |
| 15474 | + if (dssdev->type == OMAP_DISPLAY_TYPE_VENC) { |
| 15475 | + if (!tv_mgr->device || force) { |
| 15476 | + if (tv_mgr->device) |
| 15477 | + tv_mgr->unset_device(tv_mgr); |
| 15478 | + tv_mgr->set_device(tv_mgr, dssdev); |
| 15479 | + mgr = tv_mgr; |
| 15480 | + } |
| 15481 | + } |
| 15482 | + |
| 15483 | + if (mgr) { |
| 15484 | + for (i = 0; i < 3; i++) { |
| 15485 | + struct omap_overlay *ovl; |
| 15486 | + ovl = omap_dss_get_overlay(i); |
| 15487 | + if (!ovl->manager || force) { |
| 15488 | + if (ovl->manager) |
| 15489 | + omap_dss_unset_manager(ovl); |
| 15490 | + omap_dss_set_manager(ovl, mgr); |
| 15491 | + } |
| 15492 | + } |
| 15493 | + } |
| 15494 | +} |
| 15495 | + |
| 15496 | +void dss_uninit_overlays(struct platform_device *pdev) |
| 15497 | +{ |
| 15498 | + struct omap_overlay *ovl; |
| 15499 | + |
| 15500 | + while (!list_empty(&overlay_list)) { |
| 15501 | + ovl = list_first_entry(&overlay_list, |
| 15502 | + struct omap_overlay, list); |
| 15503 | + list_del(&ovl->list); |
| 15504 | + kobject_del(&ovl->kobj); |
| 15505 | + kobject_put(&ovl->kobj); |
| 15506 | + kfree(ovl); |
| 15507 | + } |
| 15508 | + |
| 15509 | + num_overlays = 0; |
| 15510 | +} |
| 15511 | + |
| 15512 | --- /dev/null |
| 15513 | +++ b/drivers/video/omap2/dss/rfbi.c |
| 15514 | @@ -0,0 +1,1310 @@ |
| 15515 | +/* |
| 15516 | + * linux/drivers/video/omap2/dss/rfbi.c |
| 15517 | + * |
| 15518 | + * Copyright (C) 2009 Nokia Corporation |
| 15519 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 15520 | + * |
| 15521 | + * Some code and ideas taken from drivers/video/omap/ driver |
| 15522 | + * by Imre Deak. |
| 15523 | + * |
| 15524 | + * This program is free software; you can redistribute it and/or modify it |
| 15525 | + * under the terms of the GNU General Public License version 2 as published by |
| 15526 | + * the Free Software Foundation. |
| 15527 | + * |
| 15528 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 15529 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 15530 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 15531 | + * more details. |
| 15532 | + * |
| 15533 | + * You should have received a copy of the GNU General Public License along with |
| 15534 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 15535 | + */ |
| 15536 | + |
| 15537 | +#define DSS_SUBSYS_NAME "RFBI" |
| 15538 | + |
| 15539 | +#include <linux/kernel.h> |
| 15540 | +#include <linux/dma-mapping.h> |
| 15541 | +#include <linux/vmalloc.h> |
| 15542 | +#include <linux/clk.h> |
| 15543 | +#include <linux/io.h> |
| 15544 | +#include <linux/delay.h> |
| 15545 | +#include <linux/kfifo.h> |
| 15546 | +#include <linux/ktime.h> |
| 15547 | +#include <linux/hrtimer.h> |
| 15548 | +#include <linux/seq_file.h> |
| 15549 | + |
| 15550 | +#include <mach/board.h> |
| 15551 | +#include <mach/display.h> |
| 15552 | +#include "dss.h" |
| 15553 | + |
| 15554 | +/*#define MEASURE_PERF*/ |
| 15555 | + |
| 15556 | +#define RFBI_BASE 0x48050800 |
| 15557 | + |
| 15558 | +struct rfbi_reg { u16 idx; }; |
| 15559 | + |
| 15560 | +#define RFBI_REG(idx) ((const struct rfbi_reg) { idx }) |
| 15561 | + |
| 15562 | +#define RFBI_REVISION RFBI_REG(0x0000) |
| 15563 | +#define RFBI_SYSCONFIG RFBI_REG(0x0010) |
| 15564 | +#define RFBI_SYSSTATUS RFBI_REG(0x0014) |
| 15565 | +#define RFBI_CONTROL RFBI_REG(0x0040) |
| 15566 | +#define RFBI_PIXEL_CNT RFBI_REG(0x0044) |
| 15567 | +#define RFBI_LINE_NUMBER RFBI_REG(0x0048) |
| 15568 | +#define RFBI_CMD RFBI_REG(0x004c) |
| 15569 | +#define RFBI_PARAM RFBI_REG(0x0050) |
| 15570 | +#define RFBI_DATA RFBI_REG(0x0054) |
| 15571 | +#define RFBI_READ RFBI_REG(0x0058) |
| 15572 | +#define RFBI_STATUS RFBI_REG(0x005c) |
| 15573 | + |
| 15574 | +#define RFBI_CONFIG(n) RFBI_REG(0x0060 + (n)*0x18) |
| 15575 | +#define RFBI_ONOFF_TIME(n) RFBI_REG(0x0064 + (n)*0x18) |
| 15576 | +#define RFBI_CYCLE_TIME(n) RFBI_REG(0x0068 + (n)*0x18) |
| 15577 | +#define RFBI_DATA_CYCLE1(n) RFBI_REG(0x006c + (n)*0x18) |
| 15578 | +#define RFBI_DATA_CYCLE2(n) RFBI_REG(0x0070 + (n)*0x18) |
| 15579 | +#define RFBI_DATA_CYCLE3(n) RFBI_REG(0x0074 + (n)*0x18) |
| 15580 | + |
| 15581 | +#define RFBI_VSYNC_WIDTH RFBI_REG(0x0090) |
| 15582 | +#define RFBI_HSYNC_WIDTH RFBI_REG(0x0094) |
| 15583 | + |
| 15584 | +#define RFBI_CMD_FIFO_LEN_BYTES (16 * sizeof(struct update_param)) |
| 15585 | + |
| 15586 | +#define REG_FLD_MOD(idx, val, start, end) \ |
| 15587 | + rfbi_write_reg(idx, FLD_MOD(rfbi_read_reg(idx), val, start, end)) |
| 15588 | + |
| 15589 | +/* To work around an RFBI transfer rate limitation */ |
| 15590 | +#define OMAP_RFBI_RATE_LIMIT 1 |
| 15591 | + |
| 15592 | +enum omap_rfbi_cycleformat { |
| 15593 | + OMAP_DSS_RFBI_CYCLEFORMAT_1_1 = 0, |
| 15594 | + OMAP_DSS_RFBI_CYCLEFORMAT_2_1 = 1, |
| 15595 | + OMAP_DSS_RFBI_CYCLEFORMAT_3_1 = 2, |
| 15596 | + OMAP_DSS_RFBI_CYCLEFORMAT_3_2 = 3, |
| 15597 | +}; |
| 15598 | + |
| 15599 | +enum omap_rfbi_datatype { |
| 15600 | + OMAP_DSS_RFBI_DATATYPE_12 = 0, |
| 15601 | + OMAP_DSS_RFBI_DATATYPE_16 = 1, |
| 15602 | + OMAP_DSS_RFBI_DATATYPE_18 = 2, |
| 15603 | + OMAP_DSS_RFBI_DATATYPE_24 = 3, |
| 15604 | +}; |
| 15605 | + |
| 15606 | +enum omap_rfbi_parallelmode { |
| 15607 | + OMAP_DSS_RFBI_PARALLELMODE_8 = 0, |
| 15608 | + OMAP_DSS_RFBI_PARALLELMODE_9 = 1, |
| 15609 | + OMAP_DSS_RFBI_PARALLELMODE_12 = 2, |
| 15610 | + OMAP_DSS_RFBI_PARALLELMODE_16 = 3, |
| 15611 | +}; |
| 15612 | + |
| 15613 | +enum update_cmd { |
| 15614 | + RFBI_CMD_UPDATE = 0, |
| 15615 | + RFBI_CMD_SYNC = 1, |
| 15616 | +}; |
| 15617 | + |
| 15618 | +static int rfbi_convert_timings(struct rfbi_timings *t); |
| 15619 | +static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div); |
| 15620 | +static void process_cmd_fifo(void); |
| 15621 | + |
| 15622 | +static struct { |
| 15623 | + void __iomem *base; |
| 15624 | + |
| 15625 | + unsigned long l4_khz; |
| 15626 | + |
| 15627 | + enum omap_rfbi_datatype datatype; |
| 15628 | + enum omap_rfbi_parallelmode parallelmode; |
| 15629 | + |
| 15630 | + enum omap_rfbi_te_mode te_mode; |
| 15631 | + int te_enabled; |
| 15632 | + |
| 15633 | + void (*framedone_callback)(void *data); |
| 15634 | + void *framedone_callback_data; |
| 15635 | + |
| 15636 | + struct omap_dss_device *dssdev[2]; |
| 15637 | + |
| 15638 | + struct kfifo *cmd_fifo; |
| 15639 | + spinlock_t cmd_lock; |
| 15640 | + struct completion cmd_done; |
| 15641 | + atomic_t cmd_fifo_full; |
| 15642 | + atomic_t cmd_pending; |
| 15643 | +#ifdef MEASURE_PERF |
| 15644 | + unsigned perf_bytes; |
| 15645 | + ktime_t perf_setup_time; |
| 15646 | + ktime_t perf_start_time; |
| 15647 | +#endif |
| 15648 | +} rfbi; |
| 15649 | + |
| 15650 | +struct update_region { |
| 15651 | + u16 x; |
| 15652 | + u16 y; |
| 15653 | + u16 w; |
| 15654 | + u16 h; |
| 15655 | +}; |
| 15656 | + |
| 15657 | +struct update_param { |
| 15658 | + u8 rfbi_module; |
| 15659 | + u8 cmd; |
| 15660 | + |
| 15661 | + union { |
| 15662 | + struct update_region r; |
| 15663 | + struct completion *sync; |
| 15664 | + } par; |
| 15665 | +}; |
| 15666 | + |
| 15667 | +static inline void rfbi_write_reg(const struct rfbi_reg idx, u32 val) |
| 15668 | +{ |
| 15669 | + __raw_writel(val, rfbi.base + idx.idx); |
| 15670 | +} |
| 15671 | + |
| 15672 | +static inline u32 rfbi_read_reg(const struct rfbi_reg idx) |
| 15673 | +{ |
| 15674 | + return __raw_readl(rfbi.base + idx.idx); |
| 15675 | +} |
| 15676 | + |
| 15677 | +static void rfbi_enable_clocks(bool enable) |
| 15678 | +{ |
| 15679 | + if (enable) |
| 15680 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 15681 | + else |
| 15682 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 15683 | +} |
| 15684 | + |
| 15685 | +void omap_rfbi_write_command(const void *buf, u32 len) |
| 15686 | +{ |
| 15687 | + rfbi_enable_clocks(1); |
| 15688 | + switch (rfbi.parallelmode) { |
| 15689 | + case OMAP_DSS_RFBI_PARALLELMODE_8: |
| 15690 | + { |
| 15691 | + const u8 *b = buf; |
| 15692 | + for (; len; len--) |
| 15693 | + rfbi_write_reg(RFBI_CMD, *b++); |
| 15694 | + break; |
| 15695 | + } |
| 15696 | + |
| 15697 | + case OMAP_DSS_RFBI_PARALLELMODE_16: |
| 15698 | + { |
| 15699 | + const u16 *w = buf; |
| 15700 | + BUG_ON(len & 1); |
| 15701 | + for (; len; len -= 2) |
| 15702 | + rfbi_write_reg(RFBI_CMD, *w++); |
| 15703 | + break; |
| 15704 | + } |
| 15705 | + |
| 15706 | + case OMAP_DSS_RFBI_PARALLELMODE_9: |
| 15707 | + case OMAP_DSS_RFBI_PARALLELMODE_12: |
| 15708 | + default: |
| 15709 | + BUG(); |
| 15710 | + } |
| 15711 | + rfbi_enable_clocks(0); |
| 15712 | +} |
| 15713 | +EXPORT_SYMBOL(omap_rfbi_write_command); |
| 15714 | + |
| 15715 | +void omap_rfbi_read_data(void *buf, u32 len) |
| 15716 | +{ |
| 15717 | + rfbi_enable_clocks(1); |
| 15718 | + switch (rfbi.parallelmode) { |
| 15719 | + case OMAP_DSS_RFBI_PARALLELMODE_8: |
| 15720 | + { |
| 15721 | + u8 *b = buf; |
| 15722 | + for (; len; len--) { |
| 15723 | + rfbi_write_reg(RFBI_READ, 0); |
| 15724 | + *b++ = rfbi_read_reg(RFBI_READ); |
| 15725 | + } |
| 15726 | + break; |
| 15727 | + } |
| 15728 | + |
| 15729 | + case OMAP_DSS_RFBI_PARALLELMODE_16: |
| 15730 | + { |
| 15731 | + u16 *w = buf; |
| 15732 | + BUG_ON(len & ~1); |
| 15733 | + for (; len; len -= 2) { |
| 15734 | + rfbi_write_reg(RFBI_READ, 0); |
| 15735 | + *w++ = rfbi_read_reg(RFBI_READ); |
| 15736 | + } |
| 15737 | + break; |
| 15738 | + } |
| 15739 | + |
| 15740 | + case OMAP_DSS_RFBI_PARALLELMODE_9: |
| 15741 | + case OMAP_DSS_RFBI_PARALLELMODE_12: |
| 15742 | + default: |
| 15743 | + BUG(); |
| 15744 | + } |
| 15745 | + rfbi_enable_clocks(0); |
| 15746 | +} |
| 15747 | +EXPORT_SYMBOL(omap_rfbi_read_data); |
| 15748 | + |
| 15749 | +void omap_rfbi_write_data(const void *buf, u32 len) |
| 15750 | +{ |
| 15751 | + rfbi_enable_clocks(1); |
| 15752 | + switch (rfbi.parallelmode) { |
| 15753 | + case OMAP_DSS_RFBI_PARALLELMODE_8: |
| 15754 | + { |
| 15755 | + const u8 *b = buf; |
| 15756 | + for (; len; len--) |
| 15757 | + rfbi_write_reg(RFBI_PARAM, *b++); |
| 15758 | + break; |
| 15759 | + } |
| 15760 | + |
| 15761 | + case OMAP_DSS_RFBI_PARALLELMODE_16: |
| 15762 | + { |
| 15763 | + const u16 *w = buf; |
| 15764 | + BUG_ON(len & 1); |
| 15765 | + for (; len; len -= 2) |
| 15766 | + rfbi_write_reg(RFBI_PARAM, *w++); |
| 15767 | + break; |
| 15768 | + } |
| 15769 | + |
| 15770 | + case OMAP_DSS_RFBI_PARALLELMODE_9: |
| 15771 | + case OMAP_DSS_RFBI_PARALLELMODE_12: |
| 15772 | + default: |
| 15773 | + BUG(); |
| 15774 | + |
| 15775 | + } |
| 15776 | + rfbi_enable_clocks(0); |
| 15777 | +} |
| 15778 | +EXPORT_SYMBOL(omap_rfbi_write_data); |
| 15779 | + |
| 15780 | +void omap_rfbi_write_pixels(const void __iomem *buf, int scr_width, |
| 15781 | + u16 x, u16 y, |
| 15782 | + u16 w, u16 h) |
| 15783 | +{ |
| 15784 | + int start_offset = scr_width * y + x; |
| 15785 | + int horiz_offset = scr_width - w; |
| 15786 | + int i; |
| 15787 | + |
| 15788 | + rfbi_enable_clocks(1); |
| 15789 | + |
| 15790 | + if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 && |
| 15791 | + rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) { |
| 15792 | + const u16 __iomem *pd = buf; |
| 15793 | + pd += start_offset; |
| 15794 | + |
| 15795 | + for (; h; --h) { |
| 15796 | + for (i = 0; i < w; ++i) { |
| 15797 | + const u8 __iomem *b = (const u8 __iomem *)pd; |
| 15798 | + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+1)); |
| 15799 | + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+0)); |
| 15800 | + ++pd; |
| 15801 | + } |
| 15802 | + pd += horiz_offset; |
| 15803 | + } |
| 15804 | + } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_24 && |
| 15805 | + rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) { |
| 15806 | + const u32 __iomem *pd = buf; |
| 15807 | + pd += start_offset; |
| 15808 | + |
| 15809 | + for (; h; --h) { |
| 15810 | + for (i = 0; i < w; ++i) { |
| 15811 | + const u8 __iomem *b = (const u8 __iomem *)pd; |
| 15812 | + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+2)); |
| 15813 | + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+1)); |
| 15814 | + rfbi_write_reg(RFBI_PARAM, __raw_readb(b+0)); |
| 15815 | + ++pd; |
| 15816 | + } |
| 15817 | + pd += horiz_offset; |
| 15818 | + } |
| 15819 | + } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 && |
| 15820 | + rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_16) { |
| 15821 | + const u16 __iomem *pd = buf; |
| 15822 | + pd += start_offset; |
| 15823 | + |
| 15824 | + for (; h; --h) { |
| 15825 | + for (i = 0; i < w; ++i) { |
| 15826 | + rfbi_write_reg(RFBI_PARAM, __raw_readw(pd)); |
| 15827 | + ++pd; |
| 15828 | + } |
| 15829 | + pd += horiz_offset; |
| 15830 | + } |
| 15831 | + } else { |
| 15832 | + BUG(); |
| 15833 | + } |
| 15834 | + |
| 15835 | + rfbi_enable_clocks(0); |
| 15836 | +} |
| 15837 | +EXPORT_SYMBOL(omap_rfbi_write_pixels); |
| 15838 | + |
| 15839 | +#ifdef MEASURE_PERF |
| 15840 | +static void perf_mark_setup(void) |
| 15841 | +{ |
| 15842 | + rfbi.perf_setup_time = ktime_get(); |
| 15843 | +} |
| 15844 | + |
| 15845 | +static void perf_mark_start(void) |
| 15846 | +{ |
| 15847 | + rfbi.perf_start_time = ktime_get(); |
| 15848 | +} |
| 15849 | + |
| 15850 | +static void perf_show(const char *name) |
| 15851 | +{ |
| 15852 | + ktime_t t, setup_time, trans_time; |
| 15853 | + u32 total_bytes; |
| 15854 | + u32 setup_us, trans_us, total_us; |
| 15855 | + |
| 15856 | + t = ktime_get(); |
| 15857 | + |
| 15858 | + setup_time = ktime_sub(rfbi.perf_start_time, rfbi.perf_setup_time); |
| 15859 | + setup_us = (u32)ktime_to_us(setup_time); |
| 15860 | + if (setup_us == 0) |
| 15861 | + setup_us = 1; |
| 15862 | + |
| 15863 | + trans_time = ktime_sub(t, rfbi.perf_start_time); |
| 15864 | + trans_us = (u32)ktime_to_us(trans_time); |
| 15865 | + if (trans_us == 0) |
| 15866 | + trans_us = 1; |
| 15867 | + |
| 15868 | + total_us = setup_us + trans_us; |
| 15869 | + |
| 15870 | + total_bytes = rfbi.perf_bytes; |
| 15871 | + |
| 15872 | + DSSINFO("%s update %u us + %u us = %u us (%uHz), %u bytes, " |
| 15873 | + "%u kbytes/sec\n", |
| 15874 | + name, |
| 15875 | + setup_us, |
| 15876 | + trans_us, |
| 15877 | + total_us, |
| 15878 | + 1000*1000 / total_us, |
| 15879 | + total_bytes, |
| 15880 | + total_bytes * 1000 / total_us); |
| 15881 | +} |
| 15882 | +#else |
| 15883 | +#define perf_mark_setup() |
| 15884 | +#define perf_mark_start() |
| 15885 | +#define perf_show(x) |
| 15886 | +#endif |
| 15887 | + |
| 15888 | +void rfbi_transfer_area(u16 width, u16 height, |
| 15889 | + void (callback)(void *data), void *data) |
| 15890 | +{ |
| 15891 | + u32 l; |
| 15892 | + |
| 15893 | + /*BUG_ON(callback == 0);*/ |
| 15894 | + BUG_ON(rfbi.framedone_callback != NULL); |
| 15895 | + |
| 15896 | + DSSDBG("rfbi_transfer_area %dx%d\n", width, height); |
| 15897 | + |
| 15898 | + dispc_set_lcd_size(width, height); |
| 15899 | + |
| 15900 | + dispc_enable_lcd_out(1); |
| 15901 | + |
| 15902 | + rfbi.framedone_callback = callback; |
| 15903 | + rfbi.framedone_callback_data = data; |
| 15904 | + |
| 15905 | + rfbi_enable_clocks(1); |
| 15906 | + |
| 15907 | + rfbi_write_reg(RFBI_PIXEL_CNT, width * height); |
| 15908 | + |
| 15909 | + l = rfbi_read_reg(RFBI_CONTROL); |
| 15910 | + l = FLD_MOD(l, 1, 0, 0); /* enable */ |
| 15911 | + if (!rfbi.te_enabled) |
| 15912 | + l = FLD_MOD(l, 1, 4, 4); /* ITE */ |
| 15913 | + |
| 15914 | + perf_mark_start(); |
| 15915 | + |
| 15916 | + rfbi_write_reg(RFBI_CONTROL, l); |
| 15917 | +} |
| 15918 | + |
| 15919 | +static void framedone_callback(void *data, u32 mask) |
| 15920 | +{ |
| 15921 | + void (*callback)(void *data); |
| 15922 | + |
| 15923 | + DSSDBG("FRAMEDONE\n"); |
| 15924 | + |
| 15925 | + perf_show("DISPC"); |
| 15926 | + |
| 15927 | + REG_FLD_MOD(RFBI_CONTROL, 0, 0, 0); |
| 15928 | + |
| 15929 | + rfbi_enable_clocks(0); |
| 15930 | + |
| 15931 | + callback = rfbi.framedone_callback; |
| 15932 | + rfbi.framedone_callback = NULL; |
| 15933 | + |
| 15934 | + /*callback(rfbi.framedone_callback_data);*/ |
| 15935 | + |
| 15936 | + atomic_set(&rfbi.cmd_pending, 0); |
| 15937 | + |
| 15938 | + process_cmd_fifo(); |
| 15939 | +} |
| 15940 | + |
| 15941 | +#if 1 /* VERBOSE */ |
| 15942 | +static void rfbi_print_timings(void) |
| 15943 | +{ |
| 15944 | + u32 l; |
| 15945 | + u32 time; |
| 15946 | + |
| 15947 | + l = rfbi_read_reg(RFBI_CONFIG(0)); |
| 15948 | + time = 1000000000 / rfbi.l4_khz; |
| 15949 | + if (l & (1 << 4)) |
| 15950 | + time *= 2; |
| 15951 | + |
| 15952 | + DSSDBG("Tick time %u ps\n", time); |
| 15953 | + l = rfbi_read_reg(RFBI_ONOFF_TIME(0)); |
| 15954 | + DSSDBG("CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, " |
| 15955 | + "REONTIME %d, REOFFTIME %d\n", |
| 15956 | + l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f, |
| 15957 | + (l >> 20) & 0x0f, (l >> 24) & 0x3f); |
| 15958 | + |
| 15959 | + l = rfbi_read_reg(RFBI_CYCLE_TIME(0)); |
| 15960 | + DSSDBG("WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, " |
| 15961 | + "ACCESSTIME %d\n", |
| 15962 | + (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f, |
| 15963 | + (l >> 22) & 0x3f); |
| 15964 | +} |
| 15965 | +#else |
| 15966 | +static void rfbi_print_timings(void) {} |
| 15967 | +#endif |
| 15968 | + |
| 15969 | + |
| 15970 | + |
| 15971 | + |
| 15972 | +static u32 extif_clk_period; |
| 15973 | + |
| 15974 | +static inline unsigned long round_to_extif_ticks(unsigned long ps, int div) |
| 15975 | +{ |
| 15976 | + int bus_tick = extif_clk_period * div; |
| 15977 | + return (ps + bus_tick - 1) / bus_tick * bus_tick; |
| 15978 | +} |
| 15979 | + |
| 15980 | +static int calc_reg_timing(struct rfbi_timings *t, int div) |
| 15981 | +{ |
| 15982 | + t->clk_div = div; |
| 15983 | + |
| 15984 | + t->cs_on_time = round_to_extif_ticks(t->cs_on_time, div); |
| 15985 | + |
| 15986 | + t->we_on_time = round_to_extif_ticks(t->we_on_time, div); |
| 15987 | + t->we_off_time = round_to_extif_ticks(t->we_off_time, div); |
| 15988 | + t->we_cycle_time = round_to_extif_ticks(t->we_cycle_time, div); |
| 15989 | + |
| 15990 | + t->re_on_time = round_to_extif_ticks(t->re_on_time, div); |
| 15991 | + t->re_off_time = round_to_extif_ticks(t->re_off_time, div); |
| 15992 | + t->re_cycle_time = round_to_extif_ticks(t->re_cycle_time, div); |
| 15993 | + |
| 15994 | + t->access_time = round_to_extif_ticks(t->access_time, div); |
| 15995 | + t->cs_off_time = round_to_extif_ticks(t->cs_off_time, div); |
| 15996 | + t->cs_pulse_width = round_to_extif_ticks(t->cs_pulse_width, div); |
| 15997 | + |
| 15998 | + DSSDBG("[reg]cson %d csoff %d reon %d reoff %d\n", |
| 15999 | + t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); |
| 16000 | + DSSDBG("[reg]weon %d weoff %d recyc %d wecyc %d\n", |
| 16001 | + t->we_on_time, t->we_off_time, t->re_cycle_time, |
| 16002 | + t->we_cycle_time); |
| 16003 | + DSSDBG("[reg]rdaccess %d cspulse %d\n", |
| 16004 | + t->access_time, t->cs_pulse_width); |
| 16005 | + |
| 16006 | + return rfbi_convert_timings(t); |
| 16007 | +} |
| 16008 | + |
| 16009 | +static int calc_extif_timings(struct rfbi_timings *t) |
| 16010 | +{ |
| 16011 | + u32 max_clk_div; |
| 16012 | + int div; |
| 16013 | + |
| 16014 | + rfbi_get_clk_info(&extif_clk_period, &max_clk_div); |
| 16015 | + for (div = 1; div <= max_clk_div; div++) { |
| 16016 | + if (calc_reg_timing(t, div) == 0) |
| 16017 | + break; |
| 16018 | + } |
| 16019 | + |
| 16020 | + if (div <= max_clk_div) |
| 16021 | + return 0; |
| 16022 | + |
| 16023 | + DSSERR("can't setup timings\n"); |
| 16024 | + return -1; |
| 16025 | +} |
| 16026 | + |
| 16027 | + |
| 16028 | +void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t) |
| 16029 | +{ |
| 16030 | + int r; |
| 16031 | + |
| 16032 | + if (!t->converted) { |
| 16033 | + r = calc_extif_timings(t); |
| 16034 | + if (r < 0) |
| 16035 | + DSSERR("Failed to calc timings\n"); |
| 16036 | + } |
| 16037 | + |
| 16038 | + BUG_ON(!t->converted); |
| 16039 | + |
| 16040 | + rfbi_enable_clocks(1); |
| 16041 | + rfbi_write_reg(RFBI_ONOFF_TIME(rfbi_module), t->tim[0]); |
| 16042 | + rfbi_write_reg(RFBI_CYCLE_TIME(rfbi_module), t->tim[1]); |
| 16043 | + |
| 16044 | + /* TIMEGRANULARITY */ |
| 16045 | + REG_FLD_MOD(RFBI_CONFIG(rfbi_module), |
| 16046 | + (t->tim[2] ? 1 : 0), 4, 4); |
| 16047 | + |
| 16048 | + rfbi_print_timings(); |
| 16049 | + rfbi_enable_clocks(0); |
| 16050 | +} |
| 16051 | + |
| 16052 | +static int ps_to_rfbi_ticks(int time, int div) |
| 16053 | +{ |
| 16054 | + unsigned long tick_ps; |
| 16055 | + int ret; |
| 16056 | + |
| 16057 | + /* Calculate in picosecs to yield more exact results */ |
| 16058 | + tick_ps = 1000000000 / (rfbi.l4_khz) * div; |
| 16059 | + |
| 16060 | + ret = (time + tick_ps - 1) / tick_ps; |
| 16061 | + |
| 16062 | + return ret; |
| 16063 | +} |
| 16064 | + |
| 16065 | +#ifdef OMAP_RFBI_RATE_LIMIT |
| 16066 | +unsigned long rfbi_get_max_tx_rate(void) |
| 16067 | +{ |
| 16068 | + unsigned long l4_rate, dss1_rate; |
| 16069 | + int min_l4_ticks = 0; |
| 16070 | + int i; |
| 16071 | + |
| 16072 | + /* According to TI this can't be calculated so make the |
| 16073 | + * adjustments for a couple of known frequencies and warn for |
| 16074 | + * others. |
| 16075 | + */ |
| 16076 | + static const struct { |
| 16077 | + unsigned long l4_clk; /* HZ */ |
| 16078 | + unsigned long dss1_clk; /* HZ */ |
| 16079 | + unsigned long min_l4_ticks; |
| 16080 | + } ftab[] = { |
| 16081 | + { 55, 132, 7, }, /* 7.86 MPix/s */ |
| 16082 | + { 110, 110, 12, }, /* 9.16 MPix/s */ |
| 16083 | + { 110, 132, 10, }, /* 11 Mpix/s */ |
| 16084 | + { 120, 120, 10, }, /* 12 Mpix/s */ |
| 16085 | + { 133, 133, 10, }, /* 13.3 Mpix/s */ |
| 16086 | + }; |
| 16087 | + |
| 16088 | + l4_rate = rfbi.l4_khz / 1000; |
| 16089 | + dss1_rate = dss_clk_get_rate(DSS_CLK_FCK1) / 1000000; |
| 16090 | + |
| 16091 | + for (i = 0; i < ARRAY_SIZE(ftab); i++) { |
| 16092 | + /* Use a window instead of an exact match, to account |
| 16093 | + * for different DPLL multiplier / divider pairs. |
| 16094 | + */ |
| 16095 | + if (abs(ftab[i].l4_clk - l4_rate) < 3 && |
| 16096 | + abs(ftab[i].dss1_clk - dss1_rate) < 3) { |
| 16097 | + min_l4_ticks = ftab[i].min_l4_ticks; |
| 16098 | + break; |
| 16099 | + } |
| 16100 | + } |
| 16101 | + if (i == ARRAY_SIZE(ftab)) { |
| 16102 | + /* Can't be sure, return anyway the maximum not |
| 16103 | + * rate-limited. This might cause a problem only for the |
| 16104 | + * tearing synchronisation. |
| 16105 | + */ |
| 16106 | + DSSERR("can't determine maximum RFBI transfer rate\n"); |
| 16107 | + return rfbi.l4_khz * 1000; |
| 16108 | + } |
| 16109 | + return rfbi.l4_khz * 1000 / min_l4_ticks; |
| 16110 | +} |
| 16111 | +#else |
| 16112 | +int rfbi_get_max_tx_rate(void) |
| 16113 | +{ |
| 16114 | + return rfbi.l4_khz * 1000; |
| 16115 | +} |
| 16116 | +#endif |
| 16117 | + |
| 16118 | +static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div) |
| 16119 | +{ |
| 16120 | + *clk_period = 1000000000 / rfbi.l4_khz; |
| 16121 | + *max_clk_div = 2; |
| 16122 | +} |
| 16123 | + |
| 16124 | +static int rfbi_convert_timings(struct rfbi_timings *t) |
| 16125 | +{ |
| 16126 | + u32 l; |
| 16127 | + int reon, reoff, weon, weoff, cson, csoff, cs_pulse; |
| 16128 | + int actim, recyc, wecyc; |
| 16129 | + int div = t->clk_div; |
| 16130 | + |
| 16131 | + if (div <= 0 || div > 2) |
| 16132 | + return -1; |
| 16133 | + |
| 16134 | + /* Make sure that after conversion it still holds that: |
| 16135 | + * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff, |
| 16136 | + * csoff > cson, csoff >= max(weoff, reoff), actim > reon |
| 16137 | + */ |
| 16138 | + weon = ps_to_rfbi_ticks(t->we_on_time, div); |
| 16139 | + weoff = ps_to_rfbi_ticks(t->we_off_time, div); |
| 16140 | + if (weoff <= weon) |
| 16141 | + weoff = weon + 1; |
| 16142 | + if (weon > 0x0f) |
| 16143 | + return -1; |
| 16144 | + if (weoff > 0x3f) |
| 16145 | + return -1; |
| 16146 | + |
| 16147 | + reon = ps_to_rfbi_ticks(t->re_on_time, div); |
| 16148 | + reoff = ps_to_rfbi_ticks(t->re_off_time, div); |
| 16149 | + if (reoff <= reon) |
| 16150 | + reoff = reon + 1; |
| 16151 | + if (reon > 0x0f) |
| 16152 | + return -1; |
| 16153 | + if (reoff > 0x3f) |
| 16154 | + return -1; |
| 16155 | + |
| 16156 | + cson = ps_to_rfbi_ticks(t->cs_on_time, div); |
| 16157 | + csoff = ps_to_rfbi_ticks(t->cs_off_time, div); |
| 16158 | + if (csoff <= cson) |
| 16159 | + csoff = cson + 1; |
| 16160 | + if (csoff < max(weoff, reoff)) |
| 16161 | + csoff = max(weoff, reoff); |
| 16162 | + if (cson > 0x0f) |
| 16163 | + return -1; |
| 16164 | + if (csoff > 0x3f) |
| 16165 | + return -1; |
| 16166 | + |
| 16167 | + l = cson; |
| 16168 | + l |= csoff << 4; |
| 16169 | + l |= weon << 10; |
| 16170 | + l |= weoff << 14; |
| 16171 | + l |= reon << 20; |
| 16172 | + l |= reoff << 24; |
| 16173 | + |
| 16174 | + t->tim[0] = l; |
| 16175 | + |
| 16176 | + actim = ps_to_rfbi_ticks(t->access_time, div); |
| 16177 | + if (actim <= reon) |
| 16178 | + actim = reon + 1; |
| 16179 | + if (actim > 0x3f) |
| 16180 | + return -1; |
| 16181 | + |
| 16182 | + wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div); |
| 16183 | + if (wecyc < weoff) |
| 16184 | + wecyc = weoff; |
| 16185 | + if (wecyc > 0x3f) |
| 16186 | + return -1; |
| 16187 | + |
| 16188 | + recyc = ps_to_rfbi_ticks(t->re_cycle_time, div); |
| 16189 | + if (recyc < reoff) |
| 16190 | + recyc = reoff; |
| 16191 | + if (recyc > 0x3f) |
| 16192 | + return -1; |
| 16193 | + |
| 16194 | + cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div); |
| 16195 | + if (cs_pulse > 0x3f) |
| 16196 | + return -1; |
| 16197 | + |
| 16198 | + l = wecyc; |
| 16199 | + l |= recyc << 6; |
| 16200 | + l |= cs_pulse << 12; |
| 16201 | + l |= actim << 22; |
| 16202 | + |
| 16203 | + t->tim[1] = l; |
| 16204 | + |
| 16205 | + t->tim[2] = div - 1; |
| 16206 | + |
| 16207 | + t->converted = 1; |
| 16208 | + |
| 16209 | + return 0; |
| 16210 | +} |
| 16211 | + |
| 16212 | +/* xxx FIX module selection missing */ |
| 16213 | +int omap_rfbi_setup_te(enum omap_rfbi_te_mode mode, |
| 16214 | + unsigned hs_pulse_time, unsigned vs_pulse_time, |
| 16215 | + int hs_pol_inv, int vs_pol_inv, int extif_div) |
| 16216 | +{ |
| 16217 | + int hs, vs; |
| 16218 | + int min; |
| 16219 | + u32 l; |
| 16220 | + |
| 16221 | + hs = ps_to_rfbi_ticks(hs_pulse_time, 1); |
| 16222 | + vs = ps_to_rfbi_ticks(vs_pulse_time, 1); |
| 16223 | + if (hs < 2) |
| 16224 | + return -EDOM; |
| 16225 | + if (mode == OMAP_DSS_RFBI_TE_MODE_2) |
| 16226 | + min = 2; |
| 16227 | + else /* OMAP_DSS_RFBI_TE_MODE_1 */ |
| 16228 | + min = 4; |
| 16229 | + if (vs < min) |
| 16230 | + return -EDOM; |
| 16231 | + if (vs == hs) |
| 16232 | + return -EINVAL; |
| 16233 | + rfbi.te_mode = mode; |
| 16234 | + DSSDBG("setup_te: mode %d hs %d vs %d hs_inv %d vs_inv %d\n", |
| 16235 | + mode, hs, vs, hs_pol_inv, vs_pol_inv); |
| 16236 | + |
| 16237 | + rfbi_enable_clocks(1); |
| 16238 | + rfbi_write_reg(RFBI_HSYNC_WIDTH, hs); |
| 16239 | + rfbi_write_reg(RFBI_VSYNC_WIDTH, vs); |
| 16240 | + |
| 16241 | + l = rfbi_read_reg(RFBI_CONFIG(0)); |
| 16242 | + if (hs_pol_inv) |
| 16243 | + l &= ~(1 << 21); |
| 16244 | + else |
| 16245 | + l |= 1 << 21; |
| 16246 | + if (vs_pol_inv) |
| 16247 | + l &= ~(1 << 20); |
| 16248 | + else |
| 16249 | + l |= 1 << 20; |
| 16250 | + rfbi_enable_clocks(0); |
| 16251 | + |
| 16252 | + return 0; |
| 16253 | +} |
| 16254 | +EXPORT_SYMBOL(omap_rfbi_setup_te); |
| 16255 | + |
| 16256 | +/* xxx FIX module selection missing */ |
| 16257 | +int omap_rfbi_enable_te(bool enable, unsigned line) |
| 16258 | +{ |
| 16259 | + u32 l; |
| 16260 | + |
| 16261 | + DSSDBG("te %d line %d mode %d\n", enable, line, rfbi.te_mode); |
| 16262 | + if (line > (1 << 11) - 1) |
| 16263 | + return -EINVAL; |
| 16264 | + |
| 16265 | + rfbi_enable_clocks(1); |
| 16266 | + l = rfbi_read_reg(RFBI_CONFIG(0)); |
| 16267 | + l &= ~(0x3 << 2); |
| 16268 | + if (enable) { |
| 16269 | + rfbi.te_enabled = 1; |
| 16270 | + l |= rfbi.te_mode << 2; |
| 16271 | + } else |
| 16272 | + rfbi.te_enabled = 0; |
| 16273 | + rfbi_write_reg(RFBI_CONFIG(0), l); |
| 16274 | + rfbi_write_reg(RFBI_LINE_NUMBER, line); |
| 16275 | + rfbi_enable_clocks(0); |
| 16276 | + |
| 16277 | + return 0; |
| 16278 | +} |
| 16279 | +EXPORT_SYMBOL(omap_rfbi_enable_te); |
| 16280 | + |
| 16281 | +#if 0 |
| 16282 | +static void rfbi_enable_config(int enable1, int enable2) |
| 16283 | +{ |
| 16284 | + u32 l; |
| 16285 | + int cs = 0; |
| 16286 | + |
| 16287 | + if (enable1) |
| 16288 | + cs |= 1<<0; |
| 16289 | + if (enable2) |
| 16290 | + cs |= 1<<1; |
| 16291 | + |
| 16292 | + rfbi_enable_clocks(1); |
| 16293 | + |
| 16294 | + l = rfbi_read_reg(RFBI_CONTROL); |
| 16295 | + |
| 16296 | + l = FLD_MOD(l, cs, 3, 2); |
| 16297 | + l = FLD_MOD(l, 0, 1, 1); |
| 16298 | + |
| 16299 | + rfbi_write_reg(RFBI_CONTROL, l); |
| 16300 | + |
| 16301 | + |
| 16302 | + l = rfbi_read_reg(RFBI_CONFIG(0)); |
| 16303 | + l = FLD_MOD(l, 0, 3, 2); /* TRIGGERMODE: ITE */ |
| 16304 | + /*l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */ |
| 16305 | + /*l |= FLD_VAL(0, 8, 7); */ /* L4FORMAT, 1pix/L4 */ |
| 16306 | + |
| 16307 | + l = FLD_MOD(l, 0, 16, 16); /* A0POLARITY */ |
| 16308 | + l = FLD_MOD(l, 1, 20, 20); /* TE_VSYNC_POLARITY */ |
| 16309 | + l = FLD_MOD(l, 1, 21, 21); /* HSYNCPOLARITY */ |
| 16310 | + |
| 16311 | + l = FLD_MOD(l, OMAP_DSS_RFBI_PARALLELMODE_8, 1, 0); |
| 16312 | + rfbi_write_reg(RFBI_CONFIG(0), l); |
| 16313 | + |
| 16314 | + rfbi_enable_clocks(0); |
| 16315 | +} |
| 16316 | +#endif |
| 16317 | + |
| 16318 | +int rfbi_configure(int rfbi_module, int bpp, int lines) |
| 16319 | +{ |
| 16320 | + u32 l; |
| 16321 | + int cycle1 = 0, cycle2 = 0, cycle3 = 0; |
| 16322 | + enum omap_rfbi_cycleformat cycleformat; |
| 16323 | + enum omap_rfbi_datatype datatype; |
| 16324 | + enum omap_rfbi_parallelmode parallelmode; |
| 16325 | + |
| 16326 | + switch (bpp) { |
| 16327 | + case 12: |
| 16328 | + datatype = OMAP_DSS_RFBI_DATATYPE_12; |
| 16329 | + break; |
| 16330 | + case 16: |
| 16331 | + datatype = OMAP_DSS_RFBI_DATATYPE_16; |
| 16332 | + break; |
| 16333 | + case 18: |
| 16334 | + datatype = OMAP_DSS_RFBI_DATATYPE_18; |
| 16335 | + break; |
| 16336 | + case 24: |
| 16337 | + datatype = OMAP_DSS_RFBI_DATATYPE_24; |
| 16338 | + break; |
| 16339 | + default: |
| 16340 | + BUG(); |
| 16341 | + return 1; |
| 16342 | + } |
| 16343 | + rfbi.datatype = datatype; |
| 16344 | + |
| 16345 | + switch (lines) { |
| 16346 | + case 8: |
| 16347 | + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_8; |
| 16348 | + break; |
| 16349 | + case 9: |
| 16350 | + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_9; |
| 16351 | + break; |
| 16352 | + case 12: |
| 16353 | + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_12; |
| 16354 | + break; |
| 16355 | + case 16: |
| 16356 | + parallelmode = OMAP_DSS_RFBI_PARALLELMODE_16; |
| 16357 | + break; |
| 16358 | + default: |
| 16359 | + BUG(); |
| 16360 | + return 1; |
| 16361 | + } |
| 16362 | + rfbi.parallelmode = parallelmode; |
| 16363 | + |
| 16364 | + if ((bpp % lines) == 0) { |
| 16365 | + switch (bpp / lines) { |
| 16366 | + case 1: |
| 16367 | + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_1_1; |
| 16368 | + break; |
| 16369 | + case 2: |
| 16370 | + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_2_1; |
| 16371 | + break; |
| 16372 | + case 3: |
| 16373 | + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_1; |
| 16374 | + break; |
| 16375 | + default: |
| 16376 | + BUG(); |
| 16377 | + return 1; |
| 16378 | + } |
| 16379 | + } else if ((2 * bpp % lines) == 0) { |
| 16380 | + if ((2 * bpp / lines) == 3) |
| 16381 | + cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_2; |
| 16382 | + else { |
| 16383 | + BUG(); |
| 16384 | + return 1; |
| 16385 | + } |
| 16386 | + } else { |
| 16387 | + BUG(); |
| 16388 | + return 1; |
| 16389 | + } |
| 16390 | + |
| 16391 | + switch (cycleformat) { |
| 16392 | + case OMAP_DSS_RFBI_CYCLEFORMAT_1_1: |
| 16393 | + cycle1 = lines; |
| 16394 | + break; |
| 16395 | + |
| 16396 | + case OMAP_DSS_RFBI_CYCLEFORMAT_2_1: |
| 16397 | + cycle1 = lines; |
| 16398 | + cycle2 = lines; |
| 16399 | + break; |
| 16400 | + |
| 16401 | + case OMAP_DSS_RFBI_CYCLEFORMAT_3_1: |
| 16402 | + cycle1 = lines; |
| 16403 | + cycle2 = lines; |
| 16404 | + cycle3 = lines; |
| 16405 | + break; |
| 16406 | + |
| 16407 | + case OMAP_DSS_RFBI_CYCLEFORMAT_3_2: |
| 16408 | + cycle1 = lines; |
| 16409 | + cycle2 = (lines / 2) | ((lines / 2) << 16); |
| 16410 | + cycle3 = (lines << 16); |
| 16411 | + break; |
| 16412 | + } |
| 16413 | + |
| 16414 | + rfbi_enable_clocks(1); |
| 16415 | + |
| 16416 | + REG_FLD_MOD(RFBI_CONTROL, 0, 3, 2); /* clear CS */ |
| 16417 | + |
| 16418 | + l = 0; |
| 16419 | + l |= FLD_VAL(parallelmode, 1, 0); |
| 16420 | + l |= FLD_VAL(0, 3, 2); /* TRIGGERMODE: ITE */ |
| 16421 | + l |= FLD_VAL(0, 4, 4); /* TIMEGRANULARITY */ |
| 16422 | + l |= FLD_VAL(datatype, 6, 5); |
| 16423 | + /* l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */ |
| 16424 | + l |= FLD_VAL(0, 8, 7); /* L4FORMAT, 1pix/L4 */ |
| 16425 | + l |= FLD_VAL(cycleformat, 10, 9); |
| 16426 | + l |= FLD_VAL(0, 12, 11); /* UNUSEDBITS */ |
| 16427 | + l |= FLD_VAL(0, 16, 16); /* A0POLARITY */ |
| 16428 | + l |= FLD_VAL(0, 17, 17); /* REPOLARITY */ |
| 16429 | + l |= FLD_VAL(0, 18, 18); /* WEPOLARITY */ |
| 16430 | + l |= FLD_VAL(0, 19, 19); /* CSPOLARITY */ |
| 16431 | + l |= FLD_VAL(1, 20, 20); /* TE_VSYNC_POLARITY */ |
| 16432 | + l |= FLD_VAL(1, 21, 21); /* HSYNCPOLARITY */ |
| 16433 | + rfbi_write_reg(RFBI_CONFIG(rfbi_module), l); |
| 16434 | + |
| 16435 | + rfbi_write_reg(RFBI_DATA_CYCLE1(rfbi_module), cycle1); |
| 16436 | + rfbi_write_reg(RFBI_DATA_CYCLE2(rfbi_module), cycle2); |
| 16437 | + rfbi_write_reg(RFBI_DATA_CYCLE3(rfbi_module), cycle3); |
| 16438 | + |
| 16439 | + |
| 16440 | + l = rfbi_read_reg(RFBI_CONTROL); |
| 16441 | + l = FLD_MOD(l, rfbi_module+1, 3, 2); /* Select CSx */ |
| 16442 | + l = FLD_MOD(l, 0, 1, 1); /* clear bypass */ |
| 16443 | + rfbi_write_reg(RFBI_CONTROL, l); |
| 16444 | + |
| 16445 | + |
| 16446 | + DSSDBG("RFBI config: bpp %d, lines %d, cycles: 0x%x 0x%x 0x%x\n", |
| 16447 | + bpp, lines, cycle1, cycle2, cycle3); |
| 16448 | + |
| 16449 | + rfbi_enable_clocks(0); |
| 16450 | + |
| 16451 | + return 0; |
| 16452 | +} |
| 16453 | +EXPORT_SYMBOL(rfbi_configure); |
| 16454 | + |
| 16455 | +static int rfbi_find_display(struct omap_dss_device *dssdev) |
| 16456 | +{ |
| 16457 | + if (dssdev == rfbi.dssdev[0]) |
| 16458 | + return 0; |
| 16459 | + |
| 16460 | + if (dssdev == rfbi.dssdev[1]) |
| 16461 | + return 1; |
| 16462 | + |
| 16463 | + BUG(); |
| 16464 | + return -1; |
| 16465 | +} |
| 16466 | + |
| 16467 | + |
| 16468 | +static void signal_fifo_waiters(void) |
| 16469 | +{ |
| 16470 | + if (atomic_read(&rfbi.cmd_fifo_full) > 0) { |
| 16471 | + /* DSSDBG("SIGNALING: Fifo not full for waiter!\n"); */ |
| 16472 | + complete(&rfbi.cmd_done); |
| 16473 | + atomic_dec(&rfbi.cmd_fifo_full); |
| 16474 | + } |
| 16475 | +} |
| 16476 | + |
| 16477 | +/* returns 1 for async op, and 0 for sync op */ |
| 16478 | +static int do_update(struct omap_dss_device *dssdev, struct update_region *upd) |
| 16479 | +{ |
| 16480 | + u16 x = upd->x; |
| 16481 | + u16 y = upd->y; |
| 16482 | + u16 w = upd->w; |
| 16483 | + u16 h = upd->h; |
| 16484 | + |
| 16485 | + perf_mark_setup(); |
| 16486 | + |
| 16487 | + if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { |
| 16488 | + /*dssdev->driver->enable_te(dssdev, 1); */ |
| 16489 | + dss_setup_partial_planes(dssdev, &x, &y, &w, &h); |
| 16490 | + } |
| 16491 | + |
| 16492 | +#ifdef MEASURE_PERF |
| 16493 | + rfbi.perf_bytes = w * h * 2; /* XXX always 16bit */ |
| 16494 | +#endif |
| 16495 | + |
| 16496 | + dssdev->driver->setup_update(dssdev, x, y, w, h); |
| 16497 | + |
| 16498 | + if (dssdev->manager->caps & OMAP_DSS_OVL_MGR_CAP_DISPC) { |
| 16499 | + rfbi_transfer_area(w, h, NULL, NULL); |
| 16500 | + return 1; |
| 16501 | + } else { |
| 16502 | + struct omap_overlay *ovl; |
| 16503 | + void __iomem *addr; |
| 16504 | + int scr_width; |
| 16505 | + |
| 16506 | + ovl = dssdev->manager->overlays[0]; |
| 16507 | + scr_width = ovl->info.screen_width; |
| 16508 | + addr = ovl->info.vaddr; |
| 16509 | + |
| 16510 | + omap_rfbi_write_pixels(addr, scr_width, x, y, w, h); |
| 16511 | + |
| 16512 | + perf_show("L4"); |
| 16513 | + |
| 16514 | + return 0; |
| 16515 | + } |
| 16516 | +} |
| 16517 | + |
| 16518 | +static void process_cmd_fifo(void) |
| 16519 | +{ |
| 16520 | + int len; |
| 16521 | + struct update_param p; |
| 16522 | + struct omap_dss_device *dssdev; |
| 16523 | + unsigned long flags; |
| 16524 | + |
| 16525 | + if (atomic_inc_return(&rfbi.cmd_pending) != 1) |
| 16526 | + return; |
| 16527 | + |
| 16528 | + while (true) { |
| 16529 | + spin_lock_irqsave(rfbi.cmd_fifo->lock, flags); |
| 16530 | + |
| 16531 | + len = __kfifo_get(rfbi.cmd_fifo, (unsigned char *)&p, |
| 16532 | + sizeof(struct update_param)); |
| 16533 | + if (len == 0) { |
| 16534 | + DSSDBG("nothing more in fifo\n"); |
| 16535 | + atomic_set(&rfbi.cmd_pending, 0); |
| 16536 | + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); |
| 16537 | + break; |
| 16538 | + } |
| 16539 | + |
| 16540 | + /* DSSDBG("fifo full %d\n", rfbi.cmd_fifo_full.counter);*/ |
| 16541 | + |
| 16542 | + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); |
| 16543 | + |
| 16544 | + BUG_ON(len != sizeof(struct update_param)); |
| 16545 | + BUG_ON(p.rfbi_module > 1); |
| 16546 | + |
| 16547 | + dssdev = rfbi.dssdev[p.rfbi_module]; |
| 16548 | + |
| 16549 | + if (p.cmd == RFBI_CMD_UPDATE) { |
| 16550 | + if (do_update(dssdev, &p.par.r)) |
| 16551 | + break; /* async op */ |
| 16552 | + } else if (p.cmd == RFBI_CMD_SYNC) { |
| 16553 | + DSSDBG("Signaling SYNC done!\n"); |
| 16554 | + complete(p.par.sync); |
| 16555 | + } else |
| 16556 | + BUG(); |
| 16557 | + } |
| 16558 | + |
| 16559 | + signal_fifo_waiters(); |
| 16560 | +} |
| 16561 | + |
| 16562 | +static void rfbi_push_cmd(struct update_param *p) |
| 16563 | +{ |
| 16564 | + int ret; |
| 16565 | + |
| 16566 | + while (1) { |
| 16567 | + unsigned long flags; |
| 16568 | + int available; |
| 16569 | + |
| 16570 | + spin_lock_irqsave(rfbi.cmd_fifo->lock, flags); |
| 16571 | + available = RFBI_CMD_FIFO_LEN_BYTES - |
| 16572 | + __kfifo_len(rfbi.cmd_fifo); |
| 16573 | + |
| 16574 | +/* DSSDBG("%d bytes left in fifo\n", available); */ |
| 16575 | + if (available < sizeof(struct update_param)) { |
| 16576 | + DSSDBG("Going to wait because FIFO FULL..\n"); |
| 16577 | + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); |
| 16578 | + atomic_inc(&rfbi.cmd_fifo_full); |
| 16579 | + wait_for_completion(&rfbi.cmd_done); |
| 16580 | + /*DSSDBG("Woke up because fifo not full anymore\n");*/ |
| 16581 | + continue; |
| 16582 | + } |
| 16583 | + |
| 16584 | + ret = __kfifo_put(rfbi.cmd_fifo, (unsigned char *)p, |
| 16585 | + sizeof(struct update_param)); |
| 16586 | +/* DSSDBG("pushed %d bytes\n", ret);*/ |
| 16587 | + |
| 16588 | + spin_unlock_irqrestore(rfbi.cmd_fifo->lock, flags); |
| 16589 | + |
| 16590 | + BUG_ON(ret != sizeof(struct update_param)); |
| 16591 | + |
| 16592 | + break; |
| 16593 | + } |
| 16594 | +} |
| 16595 | + |
| 16596 | +static void rfbi_push_update(int rfbi_module, int x, int y, int w, int h) |
| 16597 | +{ |
| 16598 | + struct update_param p; |
| 16599 | + |
| 16600 | + p.rfbi_module = rfbi_module; |
| 16601 | + p.cmd = RFBI_CMD_UPDATE; |
| 16602 | + |
| 16603 | + p.par.r.x = x; |
| 16604 | + p.par.r.y = y; |
| 16605 | + p.par.r.w = w; |
| 16606 | + p.par.r.h = h; |
| 16607 | + |
| 16608 | + DSSDBG("RFBI pushed %d,%d %dx%d\n", x, y, w, h); |
| 16609 | + |
| 16610 | + rfbi_push_cmd(&p); |
| 16611 | + |
| 16612 | + process_cmd_fifo(); |
| 16613 | +} |
| 16614 | + |
| 16615 | +static void rfbi_push_sync(int rfbi_module, struct completion *sync_comp) |
| 16616 | +{ |
| 16617 | + struct update_param p; |
| 16618 | + |
| 16619 | + p.rfbi_module = rfbi_module; |
| 16620 | + p.cmd = RFBI_CMD_SYNC; |
| 16621 | + p.par.sync = sync_comp; |
| 16622 | + |
| 16623 | + rfbi_push_cmd(&p); |
| 16624 | + |
| 16625 | + DSSDBG("RFBI sync pushed to cmd fifo\n"); |
| 16626 | + |
| 16627 | + process_cmd_fifo(); |
| 16628 | +} |
| 16629 | + |
| 16630 | +void rfbi_dump_regs(struct seq_file *s) |
| 16631 | +{ |
| 16632 | +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, rfbi_read_reg(r)) |
| 16633 | + |
| 16634 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 16635 | + |
| 16636 | + DUMPREG(RFBI_REVISION); |
| 16637 | + DUMPREG(RFBI_SYSCONFIG); |
| 16638 | + DUMPREG(RFBI_SYSSTATUS); |
| 16639 | + DUMPREG(RFBI_CONTROL); |
| 16640 | + DUMPREG(RFBI_PIXEL_CNT); |
| 16641 | + DUMPREG(RFBI_LINE_NUMBER); |
| 16642 | + DUMPREG(RFBI_CMD); |
| 16643 | + DUMPREG(RFBI_PARAM); |
| 16644 | + DUMPREG(RFBI_DATA); |
| 16645 | + DUMPREG(RFBI_READ); |
| 16646 | + DUMPREG(RFBI_STATUS); |
| 16647 | + |
| 16648 | + DUMPREG(RFBI_CONFIG(0)); |
| 16649 | + DUMPREG(RFBI_ONOFF_TIME(0)); |
| 16650 | + DUMPREG(RFBI_CYCLE_TIME(0)); |
| 16651 | + DUMPREG(RFBI_DATA_CYCLE1(0)); |
| 16652 | + DUMPREG(RFBI_DATA_CYCLE2(0)); |
| 16653 | + DUMPREG(RFBI_DATA_CYCLE3(0)); |
| 16654 | + |
| 16655 | + DUMPREG(RFBI_CONFIG(1)); |
| 16656 | + DUMPREG(RFBI_ONOFF_TIME(1)); |
| 16657 | + DUMPREG(RFBI_CYCLE_TIME(1)); |
| 16658 | + DUMPREG(RFBI_DATA_CYCLE1(1)); |
| 16659 | + DUMPREG(RFBI_DATA_CYCLE2(1)); |
| 16660 | + DUMPREG(RFBI_DATA_CYCLE3(1)); |
| 16661 | + |
| 16662 | + DUMPREG(RFBI_VSYNC_WIDTH); |
| 16663 | + DUMPREG(RFBI_HSYNC_WIDTH); |
| 16664 | + |
| 16665 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 16666 | +#undef DUMPREG |
| 16667 | +} |
| 16668 | + |
| 16669 | +int rfbi_init(void) |
| 16670 | +{ |
| 16671 | + u32 rev; |
| 16672 | + u32 l; |
| 16673 | + |
| 16674 | + spin_lock_init(&rfbi.cmd_lock); |
| 16675 | + rfbi.cmd_fifo = kfifo_alloc(RFBI_CMD_FIFO_LEN_BYTES, GFP_KERNEL, |
| 16676 | + &rfbi.cmd_lock); |
| 16677 | + if (IS_ERR(rfbi.cmd_fifo)) |
| 16678 | + return -ENOMEM; |
| 16679 | + |
| 16680 | + init_completion(&rfbi.cmd_done); |
| 16681 | + atomic_set(&rfbi.cmd_fifo_full, 0); |
| 16682 | + atomic_set(&rfbi.cmd_pending, 0); |
| 16683 | + |
| 16684 | + rfbi.base = ioremap(RFBI_BASE, SZ_256); |
| 16685 | + if (!rfbi.base) { |
| 16686 | + DSSERR("can't ioremap RFBI\n"); |
| 16687 | + return -ENOMEM; |
| 16688 | + } |
| 16689 | + |
| 16690 | + rfbi_enable_clocks(1); |
| 16691 | + |
| 16692 | + msleep(10); |
| 16693 | + |
| 16694 | + rfbi.l4_khz = dss_clk_get_rate(DSS_CLK_ICK) / 1000; |
| 16695 | + |
| 16696 | + /* Enable autoidle and smart-idle */ |
| 16697 | + l = rfbi_read_reg(RFBI_SYSCONFIG); |
| 16698 | + l |= (1 << 0) | (2 << 3); |
| 16699 | + rfbi_write_reg(RFBI_SYSCONFIG, l); |
| 16700 | + |
| 16701 | + rev = rfbi_read_reg(RFBI_REVISION); |
| 16702 | + printk(KERN_INFO "OMAP RFBI rev %d.%d\n", |
| 16703 | + FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); |
| 16704 | + |
| 16705 | + rfbi_enable_clocks(0); |
| 16706 | + |
| 16707 | + return 0; |
| 16708 | +} |
| 16709 | + |
| 16710 | +void rfbi_exit(void) |
| 16711 | +{ |
| 16712 | + DSSDBG("rfbi_exit\n"); |
| 16713 | + |
| 16714 | + kfifo_free(rfbi.cmd_fifo); |
| 16715 | + |
| 16716 | + iounmap(rfbi.base); |
| 16717 | +} |
| 16718 | + |
| 16719 | +/* struct omap_display support */ |
| 16720 | +static int rfbi_display_update(struct omap_dss_device *dssdev, |
| 16721 | + u16 x, u16 y, u16 w, u16 h) |
| 16722 | +{ |
| 16723 | + int rfbi_module; |
| 16724 | + |
| 16725 | + if (w == 0 || h == 0) |
| 16726 | + return 0; |
| 16727 | + |
| 16728 | + rfbi_module = rfbi_find_display(dssdev); |
| 16729 | + |
| 16730 | + rfbi_push_update(rfbi_module, x, y, w, h); |
| 16731 | + |
| 16732 | + return 0; |
| 16733 | +} |
| 16734 | + |
| 16735 | +static int rfbi_display_sync(struct omap_dss_device *dssdev) |
| 16736 | +{ |
| 16737 | + struct completion sync_comp; |
| 16738 | + int rfbi_module; |
| 16739 | + |
| 16740 | + rfbi_module = rfbi_find_display(dssdev); |
| 16741 | + |
| 16742 | + init_completion(&sync_comp); |
| 16743 | + rfbi_push_sync(rfbi_module, &sync_comp); |
| 16744 | + DSSDBG("Waiting for SYNC to happen...\n"); |
| 16745 | + wait_for_completion(&sync_comp); |
| 16746 | + DSSDBG("Released from SYNC\n"); |
| 16747 | + return 0; |
| 16748 | +} |
| 16749 | + |
| 16750 | +static int rfbi_display_enable_te(struct omap_dss_device *dssdev, bool enable) |
| 16751 | +{ |
| 16752 | + dssdev->driver->enable_te(dssdev, enable); |
| 16753 | + return 0; |
| 16754 | +} |
| 16755 | + |
| 16756 | +static int rfbi_display_enable(struct omap_dss_device *dssdev) |
| 16757 | +{ |
| 16758 | + int r; |
| 16759 | + |
| 16760 | + r = omap_dss_start_device(dssdev); |
| 16761 | + if (r) { |
| 16762 | + DSSERR("failed to start device\n"); |
| 16763 | + goto err0; |
| 16764 | + } |
| 16765 | + |
| 16766 | + r = omap_dispc_register_isr(framedone_callback, NULL, |
| 16767 | + DISPC_IRQ_FRAMEDONE); |
| 16768 | + if (r) { |
| 16769 | + DSSERR("can't get FRAMEDONE irq\n"); |
| 16770 | + goto err1; |
| 16771 | + } |
| 16772 | + |
| 16773 | + dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); |
| 16774 | + |
| 16775 | + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_RFBI); |
| 16776 | + |
| 16777 | + dispc_set_tft_data_lines(dssdev->ctrl.pixel_size); |
| 16778 | + |
| 16779 | + rfbi_configure(dssdev->phy.rfbi.channel, |
| 16780 | + dssdev->ctrl.pixel_size, |
| 16781 | + dssdev->phy.rfbi.data_lines); |
| 16782 | + |
| 16783 | + rfbi_set_timings(dssdev->phy.rfbi.channel, |
| 16784 | + &dssdev->ctrl.rfbi_timings); |
| 16785 | + |
| 16786 | + |
| 16787 | + if (dssdev->driver->enable) { |
| 16788 | + r = dssdev->driver->enable(dssdev); |
| 16789 | + if (r) |
| 16790 | + goto err2; |
| 16791 | + } |
| 16792 | + |
| 16793 | + return 0; |
| 16794 | +err2: |
| 16795 | + omap_dispc_unregister_isr(framedone_callback, NULL, |
| 16796 | + DISPC_IRQ_FRAMEDONE); |
| 16797 | +err1: |
| 16798 | + omap_dss_stop_device(dssdev); |
| 16799 | +err0: |
| 16800 | + return r; |
| 16801 | +} |
| 16802 | + |
| 16803 | +static void rfbi_display_disable(struct omap_dss_device *dssdev) |
| 16804 | +{ |
| 16805 | + dssdev->driver->disable(dssdev); |
| 16806 | + omap_dispc_unregister_isr(framedone_callback, NULL, |
| 16807 | + DISPC_IRQ_FRAMEDONE); |
| 16808 | + omap_dss_stop_device(dssdev); |
| 16809 | +} |
| 16810 | + |
| 16811 | +int rfbi_init_display(struct omap_dss_device *dssdev) |
| 16812 | +{ |
| 16813 | + dssdev->enable = rfbi_display_enable; |
| 16814 | + dssdev->disable = rfbi_display_disable; |
| 16815 | + dssdev->update = rfbi_display_update; |
| 16816 | + dssdev->sync = rfbi_display_sync; |
| 16817 | + dssdev->enable_te = rfbi_display_enable_te; |
| 16818 | + |
| 16819 | + rfbi.dssdev[dssdev->phy.rfbi.channel] = dssdev; |
| 16820 | + |
| 16821 | + dssdev->caps = OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE; |
| 16822 | + |
| 16823 | + return 0; |
| 16824 | +} |
| 16825 | --- /dev/null |
| 16826 | +++ b/drivers/video/omap2/dss/sdi.c |
| 16827 | @@ -0,0 +1,261 @@ |
| 16828 | +/* |
| 16829 | + * linux/drivers/video/omap2/dss/sdi.c |
| 16830 | + * |
| 16831 | + * Copyright (C) 2009 Nokia Corporation |
| 16832 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 16833 | + * |
| 16834 | + * This program is free software; you can redistribute it and/or modify it |
| 16835 | + * under the terms of the GNU General Public License version 2 as published by |
| 16836 | + * the Free Software Foundation. |
| 16837 | + * |
| 16838 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 16839 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 16840 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 16841 | + * more details. |
| 16842 | + * |
| 16843 | + * You should have received a copy of the GNU General Public License along with |
| 16844 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 16845 | + */ |
| 16846 | + |
| 16847 | +#define DSS_SUBSYS_NAME "SDI" |
| 16848 | + |
| 16849 | +#include <linux/kernel.h> |
| 16850 | +#include <linux/clk.h> |
| 16851 | +#include <linux/delay.h> |
| 16852 | +#include <linux/err.h> |
| 16853 | + |
| 16854 | +#include <mach/board.h> |
| 16855 | +#include <mach/display.h> |
| 16856 | +#include "dss.h" |
| 16857 | + |
| 16858 | +static struct { |
| 16859 | + bool skip_init; |
| 16860 | + bool update_enabled; |
| 16861 | +} sdi; |
| 16862 | + |
| 16863 | +static void sdi_basic_init(void) |
| 16864 | +{ |
| 16865 | + dispc_set_parallel_interface_mode(OMAP_DSS_PARALLELMODE_BYPASS); |
| 16866 | + |
| 16867 | + dispc_set_lcd_display_type(OMAP_DSS_LCD_DISPLAY_TFT); |
| 16868 | + dispc_set_tft_data_lines(24); |
| 16869 | + dispc_lcd_enable_signal_polarity(1); |
| 16870 | +} |
| 16871 | + |
| 16872 | +static int sdi_display_enable(struct omap_dss_device *dssdev) |
| 16873 | +{ |
| 16874 | + struct omap_video_timings *t = &dssdev->panel.timings; |
| 16875 | + struct dispc_clock_info cinfo; |
| 16876 | + u16 lck_div, pck_div; |
| 16877 | + unsigned long fck; |
| 16878 | + unsigned long pck; |
| 16879 | + int r; |
| 16880 | + |
| 16881 | + r = omap_dss_start_device(dssdev); |
| 16882 | + if (r) { |
| 16883 | + DSSERR("failed to start device\n"); |
| 16884 | + goto err0; |
| 16885 | + } |
| 16886 | + |
| 16887 | + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { |
| 16888 | + DSSERR("dssdev already enabled\n"); |
| 16889 | + r = -EINVAL; |
| 16890 | + goto err1; |
| 16891 | + } |
| 16892 | + |
| 16893 | + /* In case of skip_init sdi_init has already enabled the clocks */ |
| 16894 | + if (!sdi.skip_init) |
| 16895 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 16896 | + |
| 16897 | + sdi_basic_init(); |
| 16898 | + |
| 16899 | + /* 15.5.9.1.2 */ |
| 16900 | + dssdev->panel.config |= OMAP_DSS_LCD_RF | OMAP_DSS_LCD_ONOFF; |
| 16901 | + |
| 16902 | + dispc_set_pol_freq(dssdev->panel.config, dssdev->panel.acbi, |
| 16903 | + dssdev->panel.acb); |
| 16904 | + |
| 16905 | + if (!sdi.skip_init) |
| 16906 | + r = dispc_calc_clock_div(1, t->pixel_clock * 1000, |
| 16907 | + &cinfo); |
| 16908 | + else |
| 16909 | + r = dispc_get_clock_div(&cinfo); |
| 16910 | + |
| 16911 | + if (r) |
| 16912 | + goto err2; |
| 16913 | + |
| 16914 | + fck = cinfo.fck; |
| 16915 | + lck_div = cinfo.lck_div; |
| 16916 | + pck_div = cinfo.pck_div; |
| 16917 | + |
| 16918 | + pck = fck / lck_div / pck_div / 1000; |
| 16919 | + |
| 16920 | + if (pck != t->pixel_clock) { |
| 16921 | + DSSWARN("Could not find exact pixel clock. Requested %d kHz, " |
| 16922 | + "got %lu kHz\n", |
| 16923 | + t->pixel_clock, pck); |
| 16924 | + |
| 16925 | + t->pixel_clock = pck; |
| 16926 | + } |
| 16927 | + |
| 16928 | + |
| 16929 | + dispc_set_lcd_timings(t); |
| 16930 | + |
| 16931 | + r = dispc_set_clock_div(&cinfo); |
| 16932 | + if (r) |
| 16933 | + goto err2; |
| 16934 | + |
| 16935 | + if (!sdi.skip_init) { |
| 16936 | + dss_sdi_init(dssdev->phy.sdi.datapairs); |
| 16937 | + dss_sdi_enable(); |
| 16938 | + mdelay(2); |
| 16939 | + } |
| 16940 | + |
| 16941 | + dispc_enable_lcd_out(1); |
| 16942 | + |
| 16943 | + if (dssdev->driver->enable) { |
| 16944 | + r = dssdev->driver->enable(dssdev); |
| 16945 | + if (r) |
| 16946 | + goto err3; |
| 16947 | + } |
| 16948 | + |
| 16949 | + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; |
| 16950 | + |
| 16951 | + sdi.skip_init = 0; |
| 16952 | + |
| 16953 | + return 0; |
| 16954 | +err3: |
| 16955 | + dispc_enable_lcd_out(0); |
| 16956 | +err2: |
| 16957 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 16958 | +err1: |
| 16959 | + omap_dss_stop_device(dssdev); |
| 16960 | +err0: |
| 16961 | + return r; |
| 16962 | +} |
| 16963 | + |
| 16964 | +static int sdi_display_resume(struct omap_dss_device *dssdev); |
| 16965 | + |
| 16966 | +static void sdi_display_disable(struct omap_dss_device *dssdev) |
| 16967 | +{ |
| 16968 | + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) |
| 16969 | + return; |
| 16970 | + |
| 16971 | + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) |
| 16972 | + sdi_display_resume(dssdev); |
| 16973 | + |
| 16974 | + if (dssdev->driver->disable) |
| 16975 | + dssdev->driver->disable(dssdev); |
| 16976 | + |
| 16977 | + dispc_enable_lcd_out(0); |
| 16978 | + |
| 16979 | + dss_sdi_disable(); |
| 16980 | + |
| 16981 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 16982 | + |
| 16983 | + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; |
| 16984 | + |
| 16985 | + omap_dss_stop_device(dssdev); |
| 16986 | +} |
| 16987 | + |
| 16988 | +static int sdi_display_suspend(struct omap_dss_device *dssdev) |
| 16989 | +{ |
| 16990 | + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) |
| 16991 | + return -EINVAL; |
| 16992 | + |
| 16993 | + if (dssdev->driver->suspend) |
| 16994 | + dssdev->driver->suspend(dssdev); |
| 16995 | + |
| 16996 | + dispc_enable_lcd_out(0); |
| 16997 | + |
| 16998 | + dss_sdi_disable(); |
| 16999 | + |
| 17000 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 17001 | + |
| 17002 | + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; |
| 17003 | + |
| 17004 | + return 0; |
| 17005 | +} |
| 17006 | + |
| 17007 | +static int sdi_display_resume(struct omap_dss_device *dssdev) |
| 17008 | +{ |
| 17009 | + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) |
| 17010 | + return -EINVAL; |
| 17011 | + |
| 17012 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 17013 | + |
| 17014 | + dss_sdi_enable(); |
| 17015 | + mdelay(2); |
| 17016 | + |
| 17017 | + dispc_enable_lcd_out(1); |
| 17018 | + |
| 17019 | + if (dssdev->driver->resume) |
| 17020 | + dssdev->driver->resume(dssdev); |
| 17021 | + |
| 17022 | + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; |
| 17023 | + |
| 17024 | + return 0; |
| 17025 | +} |
| 17026 | + |
| 17027 | +static int sdi_display_set_update_mode(struct omap_dss_device *dssdev, |
| 17028 | + enum omap_dss_update_mode mode) |
| 17029 | +{ |
| 17030 | + if (mode == OMAP_DSS_UPDATE_MANUAL) |
| 17031 | + return -EINVAL; |
| 17032 | + |
| 17033 | + if (mode == OMAP_DSS_UPDATE_DISABLED) { |
| 17034 | + dispc_enable_lcd_out(0); |
| 17035 | + sdi.update_enabled = 0; |
| 17036 | + } else { |
| 17037 | + dispc_enable_lcd_out(1); |
| 17038 | + sdi.update_enabled = 1; |
| 17039 | + } |
| 17040 | + |
| 17041 | + return 0; |
| 17042 | +} |
| 17043 | + |
| 17044 | +static enum omap_dss_update_mode sdi_display_get_update_mode( |
| 17045 | + struct omap_dss_device *dssdev) |
| 17046 | +{ |
| 17047 | + return sdi.update_enabled ? OMAP_DSS_UPDATE_AUTO : |
| 17048 | + OMAP_DSS_UPDATE_DISABLED; |
| 17049 | +} |
| 17050 | + |
| 17051 | +static void sdi_get_timings(struct omap_dss_device *dssdev, |
| 17052 | + struct omap_video_timings *timings) |
| 17053 | +{ |
| 17054 | + *timings = dssdev->panel.timings; |
| 17055 | +} |
| 17056 | + |
| 17057 | +int sdi_init_display(struct omap_dss_device *dssdev) |
| 17058 | +{ |
| 17059 | + DSSDBG("SDI init\n"); |
| 17060 | + |
| 17061 | + dssdev->enable = sdi_display_enable; |
| 17062 | + dssdev->disable = sdi_display_disable; |
| 17063 | + dssdev->suspend = sdi_display_suspend; |
| 17064 | + dssdev->resume = sdi_display_resume; |
| 17065 | + dssdev->set_update_mode = sdi_display_set_update_mode; |
| 17066 | + dssdev->get_update_mode = sdi_display_get_update_mode; |
| 17067 | + dssdev->get_timings = sdi_get_timings; |
| 17068 | + |
| 17069 | + return 0; |
| 17070 | +} |
| 17071 | + |
| 17072 | +int sdi_init(bool skip_init) |
| 17073 | +{ |
| 17074 | + /* we store this for first display enable, then clear it */ |
| 17075 | + sdi.skip_init = skip_init; |
| 17076 | + |
| 17077 | + /* |
| 17078 | + * Enable clocks already here, otherwise there would be a toggle |
| 17079 | + * of them until sdi_display_enable is called. |
| 17080 | + */ |
| 17081 | + if (skip_init) |
| 17082 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1); |
| 17083 | + return 0; |
| 17084 | +} |
| 17085 | + |
| 17086 | +void sdi_exit(void) |
| 17087 | +{ |
| 17088 | +} |
| 17089 | --- /dev/null |
| 17090 | +++ b/drivers/video/omap2/dss/venc.c |
| 17091 | @@ -0,0 +1,797 @@ |
| 17092 | +/* |
| 17093 | + * linux/drivers/video/omap2/dss/venc.c |
| 17094 | + * |
| 17095 | + * Copyright (C) 2009 Nokia Corporation |
| 17096 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 17097 | + * |
| 17098 | + * VENC settings from TI's DSS driver |
| 17099 | + * |
| 17100 | + * This program is free software; you can redistribute it and/or modify it |
| 17101 | + * under the terms of the GNU General Public License version 2 as published by |
| 17102 | + * the Free Software Foundation. |
| 17103 | + * |
| 17104 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 17105 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 17106 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 17107 | + * more details. |
| 17108 | + * |
| 17109 | + * You should have received a copy of the GNU General Public License along with |
| 17110 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 17111 | + */ |
| 17112 | + |
| 17113 | +#define DSS_SUBSYS_NAME "VENC" |
| 17114 | + |
| 17115 | +#include <linux/kernel.h> |
| 17116 | +#include <linux/module.h> |
| 17117 | +#include <linux/clk.h> |
| 17118 | +#include <linux/err.h> |
| 17119 | +#include <linux/io.h> |
| 17120 | +#include <linux/mutex.h> |
| 17121 | +#include <linux/completion.h> |
| 17122 | +#include <linux/delay.h> |
| 17123 | +#include <linux/string.h> |
| 17124 | +#include <linux/seq_file.h> |
| 17125 | +#include <linux/platform_device.h> |
| 17126 | +#include <linux/regulator/consumer.h> |
| 17127 | + |
| 17128 | +#include <mach/display.h> |
| 17129 | +#include <mach/cpu.h> |
| 17130 | + |
| 17131 | +#include "dss.h" |
| 17132 | + |
| 17133 | +#define VENC_BASE 0x48050C00 |
| 17134 | + |
| 17135 | +/* Venc registers */ |
| 17136 | +#define VENC_REV_ID 0x00 |
| 17137 | +#define VENC_STATUS 0x04 |
| 17138 | +#define VENC_F_CONTROL 0x08 |
| 17139 | +#define VENC_VIDOUT_CTRL 0x10 |
| 17140 | +#define VENC_SYNC_CTRL 0x14 |
| 17141 | +#define VENC_LLEN 0x1C |
| 17142 | +#define VENC_FLENS 0x20 |
| 17143 | +#define VENC_HFLTR_CTRL 0x24 |
| 17144 | +#define VENC_CC_CARR_WSS_CARR 0x28 |
| 17145 | +#define VENC_C_PHASE 0x2C |
| 17146 | +#define VENC_GAIN_U 0x30 |
| 17147 | +#define VENC_GAIN_V 0x34 |
| 17148 | +#define VENC_GAIN_Y 0x38 |
| 17149 | +#define VENC_BLACK_LEVEL 0x3C |
| 17150 | +#define VENC_BLANK_LEVEL 0x40 |
| 17151 | +#define VENC_X_COLOR 0x44 |
| 17152 | +#define VENC_M_CONTROL 0x48 |
| 17153 | +#define VENC_BSTAMP_WSS_DATA 0x4C |
| 17154 | +#define VENC_S_CARR 0x50 |
| 17155 | +#define VENC_LINE21 0x54 |
| 17156 | +#define VENC_LN_SEL 0x58 |
| 17157 | +#define VENC_L21__WC_CTL 0x5C |
| 17158 | +#define VENC_HTRIGGER_VTRIGGER 0x60 |
| 17159 | +#define VENC_SAVID__EAVID 0x64 |
| 17160 | +#define VENC_FLEN__FAL 0x68 |
| 17161 | +#define VENC_LAL__PHASE_RESET 0x6C |
| 17162 | +#define VENC_HS_INT_START_STOP_X 0x70 |
| 17163 | +#define VENC_HS_EXT_START_STOP_X 0x74 |
| 17164 | +#define VENC_VS_INT_START_X 0x78 |
| 17165 | +#define VENC_VS_INT_STOP_X__VS_INT_START_Y 0x7C |
| 17166 | +#define VENC_VS_INT_STOP_Y__VS_EXT_START_X 0x80 |
| 17167 | +#define VENC_VS_EXT_STOP_X__VS_EXT_START_Y 0x84 |
| 17168 | +#define VENC_VS_EXT_STOP_Y 0x88 |
| 17169 | +#define VENC_AVID_START_STOP_X 0x90 |
| 17170 | +#define VENC_AVID_START_STOP_Y 0x94 |
| 17171 | +#define VENC_FID_INT_START_X__FID_INT_START_Y 0xA0 |
| 17172 | +#define VENC_FID_INT_OFFSET_Y__FID_EXT_START_X 0xA4 |
| 17173 | +#define VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y 0xA8 |
| 17174 | +#define VENC_TVDETGP_INT_START_STOP_X 0xB0 |
| 17175 | +#define VENC_TVDETGP_INT_START_STOP_Y 0xB4 |
| 17176 | +#define VENC_GEN_CTRL 0xB8 |
| 17177 | +#define VENC_OUTPUT_CONTROL 0xC4 |
| 17178 | +#define VENC_OUTPUT_TEST 0xC8 |
| 17179 | +#define VENC_DAC_B__DAC_C 0xC8 |
| 17180 | + |
| 17181 | +struct venc_config { |
| 17182 | + u32 f_control; |
| 17183 | + u32 vidout_ctrl; |
| 17184 | + u32 sync_ctrl; |
| 17185 | + u32 llen; |
| 17186 | + u32 flens; |
| 17187 | + u32 hfltr_ctrl; |
| 17188 | + u32 cc_carr_wss_carr; |
| 17189 | + u32 c_phase; |
| 17190 | + u32 gain_u; |
| 17191 | + u32 gain_v; |
| 17192 | + u32 gain_y; |
| 17193 | + u32 black_level; |
| 17194 | + u32 blank_level; |
| 17195 | + u32 x_color; |
| 17196 | + u32 m_control; |
| 17197 | + u32 bstamp_wss_data; |
| 17198 | + u32 s_carr; |
| 17199 | + u32 line21; |
| 17200 | + u32 ln_sel; |
| 17201 | + u32 l21__wc_ctl; |
| 17202 | + u32 htrigger_vtrigger; |
| 17203 | + u32 savid__eavid; |
| 17204 | + u32 flen__fal; |
| 17205 | + u32 lal__phase_reset; |
| 17206 | + u32 hs_int_start_stop_x; |
| 17207 | + u32 hs_ext_start_stop_x; |
| 17208 | + u32 vs_int_start_x; |
| 17209 | + u32 vs_int_stop_x__vs_int_start_y; |
| 17210 | + u32 vs_int_stop_y__vs_ext_start_x; |
| 17211 | + u32 vs_ext_stop_x__vs_ext_start_y; |
| 17212 | + u32 vs_ext_stop_y; |
| 17213 | + u32 avid_start_stop_x; |
| 17214 | + u32 avid_start_stop_y; |
| 17215 | + u32 fid_int_start_x__fid_int_start_y; |
| 17216 | + u32 fid_int_offset_y__fid_ext_start_x; |
| 17217 | + u32 fid_ext_start_y__fid_ext_offset_y; |
| 17218 | + u32 tvdetgp_int_start_stop_x; |
| 17219 | + u32 tvdetgp_int_start_stop_y; |
| 17220 | + u32 gen_ctrl; |
| 17221 | +}; |
| 17222 | + |
| 17223 | +/* from TRM */ |
| 17224 | +static const struct venc_config venc_config_pal_trm = { |
| 17225 | + .f_control = 0, |
| 17226 | + .vidout_ctrl = 1, |
| 17227 | + .sync_ctrl = 0x40, |
| 17228 | + .llen = 0x35F, /* 863 */ |
| 17229 | + .flens = 0x270, /* 624 */ |
| 17230 | + .hfltr_ctrl = 0, |
| 17231 | + .cc_carr_wss_carr = 0x2F7225ED, |
| 17232 | + .c_phase = 0, |
| 17233 | + .gain_u = 0x111, |
| 17234 | + .gain_v = 0x181, |
| 17235 | + .gain_y = 0x140, |
| 17236 | + .black_level = 0x3B, |
| 17237 | + .blank_level = 0x3B, |
| 17238 | + .x_color = 0x7, |
| 17239 | + .m_control = 0x2, |
| 17240 | + .bstamp_wss_data = 0x3F, |
| 17241 | + .s_carr = 0x2A098ACB, |
| 17242 | + .line21 = 0, |
| 17243 | + .ln_sel = 0x01290015, |
| 17244 | + .l21__wc_ctl = 0x0000F603, |
| 17245 | + .htrigger_vtrigger = 0, |
| 17246 | + |
| 17247 | + .savid__eavid = 0x06A70108, |
| 17248 | + .flen__fal = 0x00180270, |
| 17249 | + .lal__phase_reset = 0x00040135, |
| 17250 | + .hs_int_start_stop_x = 0x00880358, |
| 17251 | + .hs_ext_start_stop_x = 0x000F035F, |
| 17252 | + .vs_int_start_x = 0x01A70000, |
| 17253 | + .vs_int_stop_x__vs_int_start_y = 0x000001A7, |
| 17254 | + .vs_int_stop_y__vs_ext_start_x = 0x01AF0000, |
| 17255 | + .vs_ext_stop_x__vs_ext_start_y = 0x000101AF, |
| 17256 | + .vs_ext_stop_y = 0x00000025, |
| 17257 | + .avid_start_stop_x = 0x03530083, |
| 17258 | + .avid_start_stop_y = 0x026C002E, |
| 17259 | + .fid_int_start_x__fid_int_start_y = 0x0001008A, |
| 17260 | + .fid_int_offset_y__fid_ext_start_x = 0x002E0138, |
| 17261 | + .fid_ext_start_y__fid_ext_offset_y = 0x01380001, |
| 17262 | + |
| 17263 | + .tvdetgp_int_start_stop_x = 0x00140001, |
| 17264 | + .tvdetgp_int_start_stop_y = 0x00010001, |
| 17265 | + .gen_ctrl = 0x00FF0000, |
| 17266 | +}; |
| 17267 | + |
| 17268 | +/* from TRM */ |
| 17269 | +static const struct venc_config venc_config_ntsc_trm = { |
| 17270 | + .f_control = 0, |
| 17271 | + .vidout_ctrl = 1, |
| 17272 | + .sync_ctrl = 0x8040, |
| 17273 | + .llen = 0x359, |
| 17274 | + .flens = 0x20C, |
| 17275 | + .hfltr_ctrl = 0, |
| 17276 | + .cc_carr_wss_carr = 0x043F2631, |
| 17277 | + .c_phase = 0, |
| 17278 | + .gain_u = 0x102, |
| 17279 | + .gain_v = 0x16C, |
| 17280 | + .gain_y = 0x12F, |
| 17281 | + .black_level = 0x43, |
| 17282 | + .blank_level = 0x38, |
| 17283 | + .x_color = 0x7, |
| 17284 | + .m_control = 0x1, |
| 17285 | + .bstamp_wss_data = 0x38, |
| 17286 | + .s_carr = 0x21F07C1F, |
| 17287 | + .line21 = 0, |
| 17288 | + .ln_sel = 0x01310011, |
| 17289 | + .l21__wc_ctl = 0x0000F003, |
| 17290 | + .htrigger_vtrigger = 0, |
| 17291 | + |
| 17292 | + .savid__eavid = 0x069300F4, |
| 17293 | + .flen__fal = 0x0016020C, |
| 17294 | + .lal__phase_reset = 0x00060107, |
| 17295 | + .hs_int_start_stop_x = 0x008E0350, |
| 17296 | + .hs_ext_start_stop_x = 0x000F0359, |
| 17297 | + .vs_int_start_x = 0x01A00000, |
| 17298 | + .vs_int_stop_x__vs_int_start_y = 0x020701A0, |
| 17299 | + .vs_int_stop_y__vs_ext_start_x = 0x01AC0024, |
| 17300 | + .vs_ext_stop_x__vs_ext_start_y = 0x020D01AC, |
| 17301 | + .vs_ext_stop_y = 0x00000006, |
| 17302 | + .avid_start_stop_x = 0x03480078, |
| 17303 | + .avid_start_stop_y = 0x02060024, |
| 17304 | + .fid_int_start_x__fid_int_start_y = 0x0001008A, |
| 17305 | + .fid_int_offset_y__fid_ext_start_x = 0x01AC0106, |
| 17306 | + .fid_ext_start_y__fid_ext_offset_y = 0x01060006, |
| 17307 | + |
| 17308 | + .tvdetgp_int_start_stop_x = 0x00140001, |
| 17309 | + .tvdetgp_int_start_stop_y = 0x00010001, |
| 17310 | + .gen_ctrl = 0x00F90000, |
| 17311 | +}; |
| 17312 | + |
| 17313 | +static const struct venc_config venc_config_pal_bdghi = { |
| 17314 | + .f_control = 0, |
| 17315 | + .vidout_ctrl = 0, |
| 17316 | + .sync_ctrl = 0, |
| 17317 | + .hfltr_ctrl = 0, |
| 17318 | + .x_color = 0, |
| 17319 | + .line21 = 0, |
| 17320 | + .ln_sel = 21, |
| 17321 | + .htrigger_vtrigger = 0, |
| 17322 | + .tvdetgp_int_start_stop_x = 0x00140001, |
| 17323 | + .tvdetgp_int_start_stop_y = 0x00010001, |
| 17324 | + .gen_ctrl = 0x00FB0000, |
| 17325 | + |
| 17326 | + .llen = 864-1, |
| 17327 | + .flens = 625-1, |
| 17328 | + .cc_carr_wss_carr = 0x2F7625ED, |
| 17329 | + .c_phase = 0xDF, |
| 17330 | + .gain_u = 0x111, |
| 17331 | + .gain_v = 0x181, |
| 17332 | + .gain_y = 0x140, |
| 17333 | + .black_level = 0x3e, |
| 17334 | + .blank_level = 0x3e, |
| 17335 | + .m_control = 0<<2 | 1<<1, |
| 17336 | + .bstamp_wss_data = 0x42, |
| 17337 | + .s_carr = 0x2a098acb, |
| 17338 | + .l21__wc_ctl = 0<<13 | 0x16<<8 | 0<<0, |
| 17339 | + .savid__eavid = 0x06A70108, |
| 17340 | + .flen__fal = 23<<16 | 624<<0, |
| 17341 | + .lal__phase_reset = 2<<17 | 310<<0, |
| 17342 | + .hs_int_start_stop_x = 0x00920358, |
| 17343 | + .hs_ext_start_stop_x = 0x000F035F, |
| 17344 | + .vs_int_start_x = 0x1a7<<16, |
| 17345 | + .vs_int_stop_x__vs_int_start_y = 0x000601A7, |
| 17346 | + .vs_int_stop_y__vs_ext_start_x = 0x01AF0036, |
| 17347 | + .vs_ext_stop_x__vs_ext_start_y = 0x27101af, |
| 17348 | + .vs_ext_stop_y = 0x05, |
| 17349 | + .avid_start_stop_x = 0x03530082, |
| 17350 | + .avid_start_stop_y = 0x0270002E, |
| 17351 | + .fid_int_start_x__fid_int_start_y = 0x0005008A, |
| 17352 | + .fid_int_offset_y__fid_ext_start_x = 0x002E0138, |
| 17353 | + .fid_ext_start_y__fid_ext_offset_y = 0x01380005, |
| 17354 | +}; |
| 17355 | + |
| 17356 | +const struct omap_video_timings omap_dss_pal_timings = { |
| 17357 | + .x_res = 720, |
| 17358 | + .y_res = 574, |
| 17359 | + .pixel_clock = 13500, |
| 17360 | + .hsw = 64, |
| 17361 | + .hfp = 12, |
| 17362 | + .hbp = 68, |
| 17363 | + .vsw = 5, |
| 17364 | + .vfp = 5, |
| 17365 | + .vbp = 41, |
| 17366 | +}; |
| 17367 | +EXPORT_SYMBOL(omap_dss_pal_timings); |
| 17368 | + |
| 17369 | +const struct omap_video_timings omap_dss_ntsc_timings = { |
| 17370 | + .x_res = 720, |
| 17371 | + .y_res = 482, |
| 17372 | + .pixel_clock = 13500, |
| 17373 | + .hsw = 64, |
| 17374 | + .hfp = 16, |
| 17375 | + .hbp = 58, |
| 17376 | + .vsw = 6, |
| 17377 | + .vfp = 6, |
| 17378 | + .vbp = 31, |
| 17379 | +}; |
| 17380 | +EXPORT_SYMBOL(omap_dss_ntsc_timings); |
| 17381 | + |
| 17382 | +static struct { |
| 17383 | + void __iomem *base; |
| 17384 | + struct mutex venc_lock; |
| 17385 | + u32 wss_data; |
| 17386 | + struct regulator *vdda_dac_reg; |
| 17387 | +} venc; |
| 17388 | + |
| 17389 | +static inline void venc_write_reg(int idx, u32 val) |
| 17390 | +{ |
| 17391 | + __raw_writel(val, venc.base + idx); |
| 17392 | +} |
| 17393 | + |
| 17394 | +static inline u32 venc_read_reg(int idx) |
| 17395 | +{ |
| 17396 | + u32 l = __raw_readl(venc.base + idx); |
| 17397 | + return l; |
| 17398 | +} |
| 17399 | + |
| 17400 | +static void venc_write_config(const struct venc_config *config) |
| 17401 | +{ |
| 17402 | + DSSDBG("write venc conf\n"); |
| 17403 | + |
| 17404 | + venc_write_reg(VENC_LLEN, config->llen); |
| 17405 | + venc_write_reg(VENC_FLENS, config->flens); |
| 17406 | + venc_write_reg(VENC_CC_CARR_WSS_CARR, config->cc_carr_wss_carr); |
| 17407 | + venc_write_reg(VENC_C_PHASE, config->c_phase); |
| 17408 | + venc_write_reg(VENC_GAIN_U, config->gain_u); |
| 17409 | + venc_write_reg(VENC_GAIN_V, config->gain_v); |
| 17410 | + venc_write_reg(VENC_GAIN_Y, config->gain_y); |
| 17411 | + venc_write_reg(VENC_BLACK_LEVEL, config->black_level); |
| 17412 | + venc_write_reg(VENC_BLANK_LEVEL, config->blank_level); |
| 17413 | + venc_write_reg(VENC_M_CONTROL, config->m_control); |
| 17414 | + venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data | |
| 17415 | + venc.wss_data); |
| 17416 | + venc_write_reg(VENC_S_CARR, config->s_carr); |
| 17417 | + venc_write_reg(VENC_L21__WC_CTL, config->l21__wc_ctl); |
| 17418 | + venc_write_reg(VENC_SAVID__EAVID, config->savid__eavid); |
| 17419 | + venc_write_reg(VENC_FLEN__FAL, config->flen__fal); |
| 17420 | + venc_write_reg(VENC_LAL__PHASE_RESET, config->lal__phase_reset); |
| 17421 | + venc_write_reg(VENC_HS_INT_START_STOP_X, config->hs_int_start_stop_x); |
| 17422 | + venc_write_reg(VENC_HS_EXT_START_STOP_X, config->hs_ext_start_stop_x); |
| 17423 | + venc_write_reg(VENC_VS_INT_START_X, config->vs_int_start_x); |
| 17424 | + venc_write_reg(VENC_VS_INT_STOP_X__VS_INT_START_Y, |
| 17425 | + config->vs_int_stop_x__vs_int_start_y); |
| 17426 | + venc_write_reg(VENC_VS_INT_STOP_Y__VS_EXT_START_X, |
| 17427 | + config->vs_int_stop_y__vs_ext_start_x); |
| 17428 | + venc_write_reg(VENC_VS_EXT_STOP_X__VS_EXT_START_Y, |
| 17429 | + config->vs_ext_stop_x__vs_ext_start_y); |
| 17430 | + venc_write_reg(VENC_VS_EXT_STOP_Y, config->vs_ext_stop_y); |
| 17431 | + venc_write_reg(VENC_AVID_START_STOP_X, config->avid_start_stop_x); |
| 17432 | + venc_write_reg(VENC_AVID_START_STOP_Y, config->avid_start_stop_y); |
| 17433 | + venc_write_reg(VENC_FID_INT_START_X__FID_INT_START_Y, |
| 17434 | + config->fid_int_start_x__fid_int_start_y); |
| 17435 | + venc_write_reg(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X, |
| 17436 | + config->fid_int_offset_y__fid_ext_start_x); |
| 17437 | + venc_write_reg(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y, |
| 17438 | + config->fid_ext_start_y__fid_ext_offset_y); |
| 17439 | + |
| 17440 | + venc_write_reg(VENC_DAC_B__DAC_C, venc_read_reg(VENC_DAC_B__DAC_C)); |
| 17441 | + venc_write_reg(VENC_VIDOUT_CTRL, config->vidout_ctrl); |
| 17442 | + venc_write_reg(VENC_HFLTR_CTRL, config->hfltr_ctrl); |
| 17443 | + venc_write_reg(VENC_X_COLOR, config->x_color); |
| 17444 | + venc_write_reg(VENC_LINE21, config->line21); |
| 17445 | + venc_write_reg(VENC_LN_SEL, config->ln_sel); |
| 17446 | + venc_write_reg(VENC_HTRIGGER_VTRIGGER, config->htrigger_vtrigger); |
| 17447 | + venc_write_reg(VENC_TVDETGP_INT_START_STOP_X, |
| 17448 | + config->tvdetgp_int_start_stop_x); |
| 17449 | + venc_write_reg(VENC_TVDETGP_INT_START_STOP_Y, |
| 17450 | + config->tvdetgp_int_start_stop_y); |
| 17451 | + venc_write_reg(VENC_GEN_CTRL, config->gen_ctrl); |
| 17452 | + venc_write_reg(VENC_F_CONTROL, config->f_control); |
| 17453 | + venc_write_reg(VENC_SYNC_CTRL, config->sync_ctrl); |
| 17454 | +} |
| 17455 | + |
| 17456 | +static void venc_reset(void) |
| 17457 | +{ |
| 17458 | + int t = 1000; |
| 17459 | + |
| 17460 | + venc_write_reg(VENC_F_CONTROL, 1<<8); |
| 17461 | + while (venc_read_reg(VENC_F_CONTROL) & (1<<8)) { |
| 17462 | + if (--t == 0) { |
| 17463 | + DSSERR("Failed to reset venc\n"); |
| 17464 | + return; |
| 17465 | + } |
| 17466 | + } |
| 17467 | + |
| 17468 | + /* the magical sleep that makes things work */ |
| 17469 | + msleep(20); |
| 17470 | +} |
| 17471 | + |
| 17472 | +static void venc_enable_clocks(int enable) |
| 17473 | +{ |
| 17474 | + if (enable) |
| 17475 | + dss_clk_enable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M | |
| 17476 | + DSS_CLK_96M); |
| 17477 | + else |
| 17478 | + dss_clk_disable(DSS_CLK_ICK | DSS_CLK_FCK1 | DSS_CLK_54M | |
| 17479 | + DSS_CLK_96M); |
| 17480 | +} |
| 17481 | + |
| 17482 | +static const struct venc_config *venc_timings_to_config( |
| 17483 | + struct omap_video_timings *timings) |
| 17484 | +{ |
| 17485 | + if (memcmp(&omap_dss_pal_timings, timings, sizeof(*timings)) == 0) |
| 17486 | + return &venc_config_pal_trm; |
| 17487 | + |
| 17488 | + if (memcmp(&omap_dss_ntsc_timings, timings, sizeof(*timings)) == 0) |
| 17489 | + return &venc_config_ntsc_trm; |
| 17490 | + |
| 17491 | + BUG(); |
| 17492 | +} |
| 17493 | + |
| 17494 | + |
| 17495 | + |
| 17496 | + |
| 17497 | + |
| 17498 | +/* driver */ |
| 17499 | +static int venc_panel_probe(struct omap_dss_device *dssdev) |
| 17500 | +{ |
| 17501 | + dssdev->panel.timings = omap_dss_pal_timings; |
| 17502 | + |
| 17503 | + return 0; |
| 17504 | +} |
| 17505 | + |
| 17506 | +static void venc_panel_remove(struct omap_dss_device *dssdev) |
| 17507 | +{ |
| 17508 | +} |
| 17509 | + |
| 17510 | +static int venc_panel_enable(struct omap_dss_device *dssdev) |
| 17511 | +{ |
| 17512 | + int r = 0; |
| 17513 | + |
| 17514 | + /* wait couple of vsyncs until enabling the LCD */ |
| 17515 | + msleep(50); |
| 17516 | + |
| 17517 | + if (dssdev->platform_enable) |
| 17518 | + r = dssdev->platform_enable(dssdev); |
| 17519 | + |
| 17520 | + return r; |
| 17521 | +} |
| 17522 | + |
| 17523 | +static void venc_panel_disable(struct omap_dss_device *dssdev) |
| 17524 | +{ |
| 17525 | + if (dssdev->platform_disable) |
| 17526 | + dssdev->platform_disable(dssdev); |
| 17527 | + |
| 17528 | + /* wait at least 5 vsyncs after disabling the LCD */ |
| 17529 | + |
| 17530 | + msleep(100); |
| 17531 | +} |
| 17532 | + |
| 17533 | +static int venc_panel_suspend(struct omap_dss_device *dssdev) |
| 17534 | +{ |
| 17535 | + venc_panel_disable(dssdev); |
| 17536 | + return 0; |
| 17537 | +} |
| 17538 | + |
| 17539 | +static int venc_panel_resume(struct omap_dss_device *dssdev) |
| 17540 | +{ |
| 17541 | + return venc_panel_enable(dssdev); |
| 17542 | +} |
| 17543 | + |
| 17544 | +static struct omap_dss_driver venc_driver = { |
| 17545 | + .probe = venc_panel_probe, |
| 17546 | + .remove = venc_panel_remove, |
| 17547 | + |
| 17548 | + .enable = venc_panel_enable, |
| 17549 | + .disable = venc_panel_disable, |
| 17550 | + .suspend = venc_panel_suspend, |
| 17551 | + .resume = venc_panel_resume, |
| 17552 | + |
| 17553 | + .driver = { |
| 17554 | + .name = "venc", |
| 17555 | + .owner = THIS_MODULE, |
| 17556 | + }, |
| 17557 | +}; |
| 17558 | +/* driver end */ |
| 17559 | + |
| 17560 | + |
| 17561 | + |
| 17562 | +int venc_init(struct platform_device *pdev) |
| 17563 | +{ |
| 17564 | + u8 rev_id; |
| 17565 | + |
| 17566 | + mutex_init(&venc.venc_lock); |
| 17567 | + |
| 17568 | + venc.wss_data = 0; |
| 17569 | + |
| 17570 | + venc.base = ioremap(VENC_BASE, SZ_1K); |
| 17571 | + if (!venc.base) { |
| 17572 | + DSSERR("can't ioremap VENC\n"); |
| 17573 | + return -ENOMEM; |
| 17574 | + } |
| 17575 | + |
| 17576 | + venc.vdda_dac_reg = regulator_get(&pdev->dev, "vdda_dac"); |
| 17577 | + if (IS_ERR(venc.vdda_dac_reg)) { |
| 17578 | + iounmap(venc.base); |
| 17579 | + DSSERR("can't get VDDA_DAC regulator\n"); |
| 17580 | + return PTR_ERR(venc.vdda_dac_reg); |
| 17581 | + } |
| 17582 | + |
| 17583 | + venc_enable_clocks(1); |
| 17584 | + |
| 17585 | + rev_id = (u8)(venc_read_reg(VENC_REV_ID) & 0xff); |
| 17586 | + printk(KERN_INFO "OMAP VENC rev %d\n", rev_id); |
| 17587 | + |
| 17588 | + venc_enable_clocks(0); |
| 17589 | + |
| 17590 | + return omap_dss_register_driver(&venc_driver); |
| 17591 | +} |
| 17592 | + |
| 17593 | +void venc_exit(void) |
| 17594 | +{ |
| 17595 | + omap_dss_unregister_driver(&venc_driver); |
| 17596 | + |
| 17597 | + regulator_put(venc.vdda_dac_reg); |
| 17598 | + |
| 17599 | + iounmap(venc.base); |
| 17600 | +} |
| 17601 | + |
| 17602 | +static void venc_power_on(struct omap_dss_device *dssdev) |
| 17603 | +{ |
| 17604 | + u32 l; |
| 17605 | + |
| 17606 | + venc_enable_clocks(1); |
| 17607 | + |
| 17608 | + venc_reset(); |
| 17609 | + venc_write_config(venc_timings_to_config(&dssdev->panel.timings)); |
| 17610 | + |
| 17611 | + dss_set_venc_output(dssdev->phy.venc.type); |
| 17612 | + dss_set_dac_pwrdn_bgz(1); |
| 17613 | + |
| 17614 | + l = 0; |
| 17615 | + |
| 17616 | + if (dssdev->phy.venc.type == OMAP_DSS_VENC_TYPE_COMPOSITE) |
| 17617 | + l |= 1 << 1; |
| 17618 | + else /* S-Video */ |
| 17619 | + l |= (1 << 0) | (1 << 2); |
| 17620 | + |
| 17621 | + if (dssdev->phy.venc.invert_polarity == false) |
| 17622 | + l |= 1 << 3; |
| 17623 | + |
| 17624 | + venc_write_reg(VENC_OUTPUT_CONTROL, l); |
| 17625 | + |
| 17626 | + dispc_set_digit_size(dssdev->panel.timings.x_res, |
| 17627 | + dssdev->panel.timings.y_res/2); |
| 17628 | + |
| 17629 | + regulator_enable(venc.vdda_dac_reg); |
| 17630 | + |
| 17631 | + if (dssdev->platform_enable) |
| 17632 | + dssdev->platform_enable(dssdev); |
| 17633 | + |
| 17634 | + dispc_enable_digit_out(1); |
| 17635 | +} |
| 17636 | + |
| 17637 | +static void venc_power_off(struct omap_dss_device *dssdev) |
| 17638 | +{ |
| 17639 | + venc_write_reg(VENC_OUTPUT_CONTROL, 0); |
| 17640 | + dss_set_dac_pwrdn_bgz(0); |
| 17641 | + |
| 17642 | + dispc_enable_digit_out(0); |
| 17643 | + |
| 17644 | + if (dssdev->platform_disable) |
| 17645 | + dssdev->platform_disable(dssdev); |
| 17646 | + |
| 17647 | + regulator_disable(venc.vdda_dac_reg); |
| 17648 | + |
| 17649 | + venc_enable_clocks(0); |
| 17650 | +} |
| 17651 | + |
| 17652 | +static int venc_enable_display(struct omap_dss_device *dssdev) |
| 17653 | +{ |
| 17654 | + int r = 0; |
| 17655 | + |
| 17656 | + DSSDBG("venc_enable_display\n"); |
| 17657 | + |
| 17658 | + mutex_lock(&venc.venc_lock); |
| 17659 | + |
| 17660 | + if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) { |
| 17661 | + r = -EINVAL; |
| 17662 | + goto err; |
| 17663 | + } |
| 17664 | + |
| 17665 | + venc_power_on(dssdev); |
| 17666 | + |
| 17667 | + venc.wss_data = 0; |
| 17668 | + |
| 17669 | + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; |
| 17670 | +err: |
| 17671 | + mutex_unlock(&venc.venc_lock); |
| 17672 | + |
| 17673 | + return r; |
| 17674 | +} |
| 17675 | + |
| 17676 | +static void venc_disable_display(struct omap_dss_device *dssdev) |
| 17677 | +{ |
| 17678 | + DSSDBG("venc_disable_display\n"); |
| 17679 | + |
| 17680 | + mutex_lock(&venc.venc_lock); |
| 17681 | + |
| 17682 | + if (dssdev->state == OMAP_DSS_DISPLAY_DISABLED) |
| 17683 | + goto end; |
| 17684 | + |
| 17685 | + if (dssdev->state == OMAP_DSS_DISPLAY_SUSPENDED) { |
| 17686 | + /* suspended is the same as disabled with venc */ |
| 17687 | + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; |
| 17688 | + goto end; |
| 17689 | + } |
| 17690 | + |
| 17691 | + venc_power_off(dssdev); |
| 17692 | + |
| 17693 | + dssdev->state = OMAP_DSS_DISPLAY_DISABLED; |
| 17694 | +end: |
| 17695 | + mutex_unlock(&venc.venc_lock); |
| 17696 | +} |
| 17697 | + |
| 17698 | +static int venc_display_suspend(struct omap_dss_device *dssdev) |
| 17699 | +{ |
| 17700 | + int r = 0; |
| 17701 | + |
| 17702 | + DSSDBG("venc_display_suspend\n"); |
| 17703 | + |
| 17704 | + mutex_lock(&venc.venc_lock); |
| 17705 | + |
| 17706 | + if (dssdev->state != OMAP_DSS_DISPLAY_ACTIVE) { |
| 17707 | + r = -EINVAL; |
| 17708 | + goto err; |
| 17709 | + } |
| 17710 | + |
| 17711 | + venc_power_off(dssdev); |
| 17712 | + |
| 17713 | + dssdev->state = OMAP_DSS_DISPLAY_SUSPENDED; |
| 17714 | +err: |
| 17715 | + mutex_unlock(&venc.venc_lock); |
| 17716 | + |
| 17717 | + return r; |
| 17718 | +} |
| 17719 | + |
| 17720 | +static int venc_display_resume(struct omap_dss_device *dssdev) |
| 17721 | +{ |
| 17722 | + int r = 0; |
| 17723 | + |
| 17724 | + DSSDBG("venc_display_resume\n"); |
| 17725 | + |
| 17726 | + mutex_lock(&venc.venc_lock); |
| 17727 | + |
| 17728 | + if (dssdev->state != OMAP_DSS_DISPLAY_SUSPENDED) { |
| 17729 | + r = -EINVAL; |
| 17730 | + goto err; |
| 17731 | + } |
| 17732 | + |
| 17733 | + venc_power_on(dssdev); |
| 17734 | + |
| 17735 | + dssdev->state = OMAP_DSS_DISPLAY_ACTIVE; |
| 17736 | +err: |
| 17737 | + mutex_unlock(&venc.venc_lock); |
| 17738 | + |
| 17739 | + return r; |
| 17740 | +} |
| 17741 | + |
| 17742 | +static void venc_get_timings(struct omap_dss_device *dssdev, |
| 17743 | + struct omap_video_timings *timings) |
| 17744 | +{ |
| 17745 | + *timings = dssdev->panel.timings; |
| 17746 | +} |
| 17747 | + |
| 17748 | +static void venc_set_timings(struct omap_dss_device *dssdev, |
| 17749 | + struct omap_video_timings *timings) |
| 17750 | +{ |
| 17751 | + DSSDBG("venc_set_timings\n"); |
| 17752 | + |
| 17753 | + /* Reset WSS data when the TV standard changes. */ |
| 17754 | + if (memcmp(&dssdev->panel.timings, timings, sizeof(*timings))) |
| 17755 | + venc.wss_data = 0; |
| 17756 | + |
| 17757 | + dssdev->panel.timings = *timings; |
| 17758 | + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) { |
| 17759 | + /* turn the venc off and on to get new timings to use */ |
| 17760 | + venc_disable_display(dssdev); |
| 17761 | + venc_enable_display(dssdev); |
| 17762 | + } |
| 17763 | +} |
| 17764 | + |
| 17765 | +static int venc_check_timings(struct omap_dss_device *dssdev, |
| 17766 | + struct omap_video_timings *timings) |
| 17767 | +{ |
| 17768 | + DSSDBG("venc_check_timings\n"); |
| 17769 | + |
| 17770 | + if (memcmp(&omap_dss_pal_timings, timings, sizeof(*timings)) == 0) |
| 17771 | + return 0; |
| 17772 | + |
| 17773 | + if (memcmp(&omap_dss_ntsc_timings, timings, sizeof(*timings)) == 0) |
| 17774 | + return 0; |
| 17775 | + |
| 17776 | + return -EINVAL; |
| 17777 | +} |
| 17778 | + |
| 17779 | +static u32 venc_get_wss(struct omap_dss_device *dssdev) |
| 17780 | +{ |
| 17781 | + /* Invert due to VENC_L21_WC_CTL:INV=1 */ |
| 17782 | + return (venc.wss_data >> 8) ^ 0xfffff; |
| 17783 | +} |
| 17784 | + |
| 17785 | +static int venc_set_wss(struct omap_dss_device *dssdev, u32 wss) |
| 17786 | +{ |
| 17787 | + const struct venc_config *config; |
| 17788 | + |
| 17789 | + DSSDBG("venc_set_wss\n"); |
| 17790 | + |
| 17791 | + mutex_lock(&venc.venc_lock); |
| 17792 | + |
| 17793 | + config = venc_timings_to_config(&dssdev->panel.timings); |
| 17794 | + |
| 17795 | + /* Invert due to VENC_L21_WC_CTL:INV=1 */ |
| 17796 | + venc.wss_data = (wss ^ 0xfffff) << 8; |
| 17797 | + |
| 17798 | + venc_enable_clocks(1); |
| 17799 | + |
| 17800 | + venc_write_reg(VENC_BSTAMP_WSS_DATA, config->bstamp_wss_data | |
| 17801 | + venc.wss_data); |
| 17802 | + |
| 17803 | + venc_enable_clocks(0); |
| 17804 | + |
| 17805 | + mutex_unlock(&venc.venc_lock); |
| 17806 | + |
| 17807 | + return 0; |
| 17808 | +} |
| 17809 | + |
| 17810 | +static enum omap_dss_update_mode venc_display_get_update_mode( |
| 17811 | + struct omap_dss_device *dssdev) |
| 17812 | +{ |
| 17813 | + if (dssdev->state == OMAP_DSS_DISPLAY_ACTIVE) |
| 17814 | + return OMAP_DSS_UPDATE_AUTO; |
| 17815 | + else |
| 17816 | + return OMAP_DSS_UPDATE_DISABLED; |
| 17817 | +} |
| 17818 | + |
| 17819 | +int venc_init_display(struct omap_dss_device *dssdev) |
| 17820 | +{ |
| 17821 | + DSSDBG("init_display\n"); |
| 17822 | + |
| 17823 | + dssdev->enable = venc_enable_display; |
| 17824 | + dssdev->disable = venc_disable_display; |
| 17825 | + dssdev->suspend = venc_display_suspend; |
| 17826 | + dssdev->resume = venc_display_resume; |
| 17827 | + dssdev->get_timings = venc_get_timings; |
| 17828 | + dssdev->set_timings = venc_set_timings; |
| 17829 | + dssdev->check_timings = venc_check_timings; |
| 17830 | + dssdev->get_wss = venc_get_wss; |
| 17831 | + dssdev->set_wss = venc_set_wss; |
| 17832 | + dssdev->get_update_mode = venc_display_get_update_mode; |
| 17833 | + |
| 17834 | + return 0; |
| 17835 | +} |
| 17836 | + |
| 17837 | +void venc_dump_regs(struct seq_file *s) |
| 17838 | +{ |
| 17839 | +#define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, venc_read_reg(r)) |
| 17840 | + |
| 17841 | + venc_enable_clocks(1); |
| 17842 | + |
| 17843 | + DUMPREG(VENC_F_CONTROL); |
| 17844 | + DUMPREG(VENC_VIDOUT_CTRL); |
| 17845 | + DUMPREG(VENC_SYNC_CTRL); |
| 17846 | + DUMPREG(VENC_LLEN); |
| 17847 | + DUMPREG(VENC_FLENS); |
| 17848 | + DUMPREG(VENC_HFLTR_CTRL); |
| 17849 | + DUMPREG(VENC_CC_CARR_WSS_CARR); |
| 17850 | + DUMPREG(VENC_C_PHASE); |
| 17851 | + DUMPREG(VENC_GAIN_U); |
| 17852 | + DUMPREG(VENC_GAIN_V); |
| 17853 | + DUMPREG(VENC_GAIN_Y); |
| 17854 | + DUMPREG(VENC_BLACK_LEVEL); |
| 17855 | + DUMPREG(VENC_BLANK_LEVEL); |
| 17856 | + DUMPREG(VENC_X_COLOR); |
| 17857 | + DUMPREG(VENC_M_CONTROL); |
| 17858 | + DUMPREG(VENC_BSTAMP_WSS_DATA); |
| 17859 | + DUMPREG(VENC_S_CARR); |
| 17860 | + DUMPREG(VENC_LINE21); |
| 17861 | + DUMPREG(VENC_LN_SEL); |
| 17862 | + DUMPREG(VENC_L21__WC_CTL); |
| 17863 | + DUMPREG(VENC_HTRIGGER_VTRIGGER); |
| 17864 | + DUMPREG(VENC_SAVID__EAVID); |
| 17865 | + DUMPREG(VENC_FLEN__FAL); |
| 17866 | + DUMPREG(VENC_LAL__PHASE_RESET); |
| 17867 | + DUMPREG(VENC_HS_INT_START_STOP_X); |
| 17868 | + DUMPREG(VENC_HS_EXT_START_STOP_X); |
| 17869 | + DUMPREG(VENC_VS_INT_START_X); |
| 17870 | + DUMPREG(VENC_VS_INT_STOP_X__VS_INT_START_Y); |
| 17871 | + DUMPREG(VENC_VS_INT_STOP_Y__VS_EXT_START_X); |
| 17872 | + DUMPREG(VENC_VS_EXT_STOP_X__VS_EXT_START_Y); |
| 17873 | + DUMPREG(VENC_VS_EXT_STOP_Y); |
| 17874 | + DUMPREG(VENC_AVID_START_STOP_X); |
| 17875 | + DUMPREG(VENC_AVID_START_STOP_Y); |
| 17876 | + DUMPREG(VENC_FID_INT_START_X__FID_INT_START_Y); |
| 17877 | + DUMPREG(VENC_FID_INT_OFFSET_Y__FID_EXT_START_X); |
| 17878 | + DUMPREG(VENC_FID_EXT_START_Y__FID_EXT_OFFSET_Y); |
| 17879 | + DUMPREG(VENC_TVDETGP_INT_START_STOP_X); |
| 17880 | + DUMPREG(VENC_TVDETGP_INT_START_STOP_Y); |
| 17881 | + DUMPREG(VENC_GEN_CTRL); |
| 17882 | + DUMPREG(VENC_OUTPUT_CONTROL); |
| 17883 | + DUMPREG(VENC_OUTPUT_TEST); |
| 17884 | + |
| 17885 | + venc_enable_clocks(0); |
| 17886 | + |
| 17887 | +#undef DUMPREG |
| 17888 | +} |
| 17889 | --- /dev/null |
| 17890 | +++ b/drivers/video/omap2/omapfb/Kconfig |
| 17891 | @@ -0,0 +1,37 @@ |
| 17892 | +menuconfig FB_OMAP2 |
| 17893 | + tristate "OMAP2/3 frame buffer support (EXPERIMENTAL)" |
| 17894 | + depends on FB && OMAP2_DSS |
| 17895 | + |
| 17896 | + select OMAP2_VRAM |
| 17897 | + select OMAP2_VRFB |
| 17898 | + select FB_CFB_FILLRECT |
| 17899 | + select FB_CFB_COPYAREA |
| 17900 | + select FB_CFB_IMAGEBLIT |
| 17901 | + help |
| 17902 | + Frame buffer driver for OMAP2/3 based boards. |
| 17903 | + |
| 17904 | +config FB_OMAP2_DEBUG_SUPPORT |
| 17905 | + bool "Debug support for OMAP2/3 FB" |
| 17906 | + default y |
| 17907 | + depends on FB_OMAP2 |
| 17908 | + help |
| 17909 | + Support for debug output. You have to enable the actual printing |
| 17910 | + with debug module parameter. |
| 17911 | + |
| 17912 | +config FB_OMAP2_FORCE_AUTO_UPDATE |
| 17913 | + bool "Force main display to automatic update mode" |
| 17914 | + depends on FB_OMAP2 |
| 17915 | + help |
| 17916 | + Forces main display to automatic update mode (if possible), |
| 17917 | + and also enables tearsync (if possible). By default |
| 17918 | + displays that support manual update are started in manual |
| 17919 | + update mode. |
| 17920 | + |
| 17921 | +config FB_OMAP2_NUM_FBS |
| 17922 | + int "Number of framebuffers" |
| 17923 | + range 1 10 |
| 17924 | + default 3 |
| 17925 | + depends on FB_OMAP2 |
| 17926 | + help |
| 17927 | + Select the number of framebuffers created. OMAP2/3 has 3 overlays |
| 17928 | + so normally this would be 3. |
| 17929 | --- /dev/null |
| 17930 | +++ b/drivers/video/omap2/omapfb/Makefile |
| 17931 | @@ -0,0 +1,2 @@ |
| 17932 | +obj-$(CONFIG_FB_OMAP2) += omapfb.o |
| 17933 | +omapfb-y := omapfb-main.o omapfb-sysfs.o omapfb-ioctl.o |
| 17934 | --- /dev/null |
| 17935 | +++ b/drivers/video/omap2/omapfb/omapfb-ioctl.c |
| 17936 | @@ -0,0 +1,727 @@ |
| 17937 | +/* |
| 17938 | + * linux/drivers/video/omap2/omapfb-ioctl.c |
| 17939 | + * |
| 17940 | + * Copyright (C) 2008 Nokia Corporation |
| 17941 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 17942 | + * |
| 17943 | + * Some code and ideas taken from drivers/video/omap/ driver |
| 17944 | + * by Imre Deak. |
| 17945 | + * |
| 17946 | + * This program is free software; you can redistribute it and/or modify it |
| 17947 | + * under the terms of the GNU General Public License version 2 as published by |
| 17948 | + * the Free Software Foundation. |
| 17949 | + * |
| 17950 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 17951 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 17952 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 17953 | + * more details. |
| 17954 | + * |
| 17955 | + * You should have received a copy of the GNU General Public License along with |
| 17956 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 17957 | + */ |
| 17958 | + |
| 17959 | +#include <linux/fb.h> |
| 17960 | +#include <linux/device.h> |
| 17961 | +#include <linux/uaccess.h> |
| 17962 | +#include <linux/platform_device.h> |
| 17963 | +#include <linux/mm.h> |
| 17964 | +#include <linux/omapfb.h> |
| 17965 | +#include <linux/vmalloc.h> |
| 17966 | + |
| 17967 | +#include <mach/display.h> |
| 17968 | +#include <mach/vrfb.h> |
| 17969 | +#include <mach/vram.h> |
| 17970 | + |
| 17971 | +#include "omapfb.h" |
| 17972 | + |
| 17973 | +static int omapfb_setup_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) |
| 17974 | +{ |
| 17975 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 17976 | + struct omapfb2_device *fbdev = ofbi->fbdev; |
| 17977 | + struct omap_dss_device *display = fb2display(fbi); |
| 17978 | + struct omap_overlay *ovl; |
| 17979 | + struct omap_overlay_info info; |
| 17980 | + int r = 0; |
| 17981 | + |
| 17982 | + DBG("omapfb_setup_plane\n"); |
| 17983 | + |
| 17984 | + if (ofbi->num_overlays != 1) { |
| 17985 | + r = -EINVAL; |
| 17986 | + goto out; |
| 17987 | + } |
| 17988 | + |
| 17989 | + /* XXX uses only the first overlay */ |
| 17990 | + ovl = ofbi->overlays[0]; |
| 17991 | + |
| 17992 | + if (pi->enabled && !ofbi->region.size) { |
| 17993 | + /* |
| 17994 | + * This plane's memory was freed, can't enable it |
| 17995 | + * until it's reallocated. |
| 17996 | + */ |
| 17997 | + r = -EINVAL; |
| 17998 | + goto out; |
| 17999 | + } |
| 18000 | + |
| 18001 | + ovl->get_overlay_info(ovl, &info); |
| 18002 | + |
| 18003 | + info.pos_x = pi->pos_x; |
| 18004 | + info.pos_y = pi->pos_y; |
| 18005 | + info.out_width = pi->out_width; |
| 18006 | + info.out_height = pi->out_height; |
| 18007 | + info.enabled = pi->enabled; |
| 18008 | + |
| 18009 | + r = ovl->set_overlay_info(ovl, &info); |
| 18010 | + if (r) |
| 18011 | + goto out; |
| 18012 | + |
| 18013 | + if (ovl->manager) { |
| 18014 | + r = ovl->manager->apply(ovl->manager); |
| 18015 | + if (r) |
| 18016 | + goto out; |
| 18017 | + } |
| 18018 | + |
| 18019 | + if (display) { |
| 18020 | + u16 w, h; |
| 18021 | + |
| 18022 | + if (display->sync) |
| 18023 | + display->sync(display); |
| 18024 | + |
| 18025 | + display->get_resolution(display, &w, &h); |
| 18026 | + |
| 18027 | + if (display->update) |
| 18028 | + display->update(display, 0, 0, w, h); |
| 18029 | + } |
| 18030 | + |
| 18031 | +out: |
| 18032 | + if (r) |
| 18033 | + dev_err(fbdev->dev, "setup_plane failed\n"); |
| 18034 | + return r; |
| 18035 | +} |
| 18036 | + |
| 18037 | +static int omapfb_query_plane(struct fb_info *fbi, struct omapfb_plane_info *pi) |
| 18038 | +{ |
| 18039 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 18040 | + |
| 18041 | + if (ofbi->num_overlays != 1) { |
| 18042 | + memset(pi, 0, sizeof(*pi)); |
| 18043 | + } else { |
| 18044 | + struct omap_overlay_info *ovli; |
| 18045 | + struct omap_overlay *ovl; |
| 18046 | + |
| 18047 | + ovl = ofbi->overlays[0]; |
| 18048 | + ovli = &ovl->info; |
| 18049 | + |
| 18050 | + pi->pos_x = ovli->pos_x; |
| 18051 | + pi->pos_y = ovli->pos_y; |
| 18052 | + pi->enabled = ovli->enabled; |
| 18053 | + pi->channel_out = 0; /* xxx */ |
| 18054 | + pi->mirror = 0; |
| 18055 | + pi->out_width = ovli->out_width; |
| 18056 | + pi->out_height = ovli->out_height; |
| 18057 | + } |
| 18058 | + |
| 18059 | + return 0; |
| 18060 | +} |
| 18061 | + |
| 18062 | +static int omapfb_setup_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) |
| 18063 | +{ |
| 18064 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 18065 | + struct omapfb2_device *fbdev = ofbi->fbdev; |
| 18066 | + struct omapfb2_mem_region *rg; |
| 18067 | + int r, i; |
| 18068 | + size_t size; |
| 18069 | + |
| 18070 | + if (mi->type > OMAPFB_MEMTYPE_MAX) |
| 18071 | + return -EINVAL; |
| 18072 | + |
| 18073 | + size = PAGE_ALIGN(mi->size); |
| 18074 | + |
| 18075 | + rg = &ofbi->region; |
| 18076 | + |
| 18077 | + for (i = 0; i < ofbi->num_overlays; i++) { |
| 18078 | + if (ofbi->overlays[i]->info.enabled) |
| 18079 | + return -EBUSY; |
| 18080 | + } |
| 18081 | + |
| 18082 | + if (rg->size != size || rg->type != mi->type) { |
| 18083 | + r = omapfb_realloc_fbmem(fbi, size, mi->type); |
| 18084 | + if (r) { |
| 18085 | + dev_err(fbdev->dev, "realloc fbmem failed\n"); |
| 18086 | + return r; |
| 18087 | + } |
| 18088 | + } |
| 18089 | + |
| 18090 | + return 0; |
| 18091 | +} |
| 18092 | + |
| 18093 | +static int omapfb_query_mem(struct fb_info *fbi, struct omapfb_mem_info *mi) |
| 18094 | +{ |
| 18095 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 18096 | + struct omapfb2_mem_region *rg; |
| 18097 | + |
| 18098 | + rg = &ofbi->region; |
| 18099 | + memset(mi, 0, sizeof(*mi)); |
| 18100 | + |
| 18101 | + mi->size = rg->size; |
| 18102 | + mi->type = rg->type; |
| 18103 | + |
| 18104 | + return 0; |
| 18105 | +} |
| 18106 | + |
| 18107 | +static int omapfb_update_window(struct fb_info *fbi, |
| 18108 | + u32 x, u32 y, u32 w, u32 h) |
| 18109 | +{ |
| 18110 | + struct omap_dss_device *display = fb2display(fbi); |
| 18111 | + u16 dw, dh; |
| 18112 | + |
| 18113 | + if (!display) |
| 18114 | + return 0; |
| 18115 | + |
| 18116 | + if (w == 0 || h == 0) |
| 18117 | + return 0; |
| 18118 | + |
| 18119 | + display->get_resolution(display, &dw, &dh); |
| 18120 | + |
| 18121 | + if (x + w > dw || y + h > dh) |
| 18122 | + return -EINVAL; |
| 18123 | + |
| 18124 | + display->update(display, x, y, w, h); |
| 18125 | + |
| 18126 | + return 0; |
| 18127 | +} |
| 18128 | + |
| 18129 | +static int omapfb_set_update_mode(struct fb_info *fbi, |
| 18130 | + enum omapfb_update_mode mode) |
| 18131 | +{ |
| 18132 | + struct omap_dss_device *display = fb2display(fbi); |
| 18133 | + enum omap_dss_update_mode um; |
| 18134 | + int r; |
| 18135 | + |
| 18136 | + if (!display || !display->set_update_mode) |
| 18137 | + return -EINVAL; |
| 18138 | + |
| 18139 | + switch (mode) { |
| 18140 | + case OMAPFB_UPDATE_DISABLED: |
| 18141 | + um = OMAP_DSS_UPDATE_DISABLED; |
| 18142 | + break; |
| 18143 | + |
| 18144 | + case OMAPFB_AUTO_UPDATE: |
| 18145 | + um = OMAP_DSS_UPDATE_AUTO; |
| 18146 | + break; |
| 18147 | + |
| 18148 | + case OMAPFB_MANUAL_UPDATE: |
| 18149 | + um = OMAP_DSS_UPDATE_MANUAL; |
| 18150 | + break; |
| 18151 | + |
| 18152 | + default: |
| 18153 | + return -EINVAL; |
| 18154 | + } |
| 18155 | + |
| 18156 | + r = display->set_update_mode(display, um); |
| 18157 | + |
| 18158 | + return r; |
| 18159 | +} |
| 18160 | + |
| 18161 | +static int omapfb_get_update_mode(struct fb_info *fbi, |
| 18162 | + enum omapfb_update_mode *mode) |
| 18163 | +{ |
| 18164 | + struct omap_dss_device *display = fb2display(fbi); |
| 18165 | + enum omap_dss_update_mode m; |
| 18166 | + |
| 18167 | + if (!display || !display->get_update_mode) |
| 18168 | + return -EINVAL; |
| 18169 | + |
| 18170 | + m = display->get_update_mode(display); |
| 18171 | + |
| 18172 | + switch (m) { |
| 18173 | + case OMAP_DSS_UPDATE_DISABLED: |
| 18174 | + *mode = OMAPFB_UPDATE_DISABLED; |
| 18175 | + break; |
| 18176 | + case OMAP_DSS_UPDATE_AUTO: |
| 18177 | + *mode = OMAPFB_AUTO_UPDATE; |
| 18178 | + break; |
| 18179 | + case OMAP_DSS_UPDATE_MANUAL: |
| 18180 | + *mode = OMAPFB_MANUAL_UPDATE; |
| 18181 | + break; |
| 18182 | + default: |
| 18183 | + BUG(); |
| 18184 | + } |
| 18185 | + |
| 18186 | + return 0; |
| 18187 | +} |
| 18188 | + |
| 18189 | +/* XXX this color key handling is a hack... */ |
| 18190 | +static struct omapfb_color_key omapfb_color_keys[2]; |
| 18191 | + |
| 18192 | +static int _omapfb_set_color_key(struct omap_overlay_manager *mgr, |
| 18193 | + struct omapfb_color_key *ck) |
| 18194 | +{ |
| 18195 | + struct omap_overlay_manager_info info; |
| 18196 | + enum omap_dss_trans_key_type kt; |
| 18197 | + int r; |
| 18198 | + |
| 18199 | + mgr->get_manager_info(mgr, &info); |
| 18200 | + |
| 18201 | + if (ck->key_type == OMAPFB_COLOR_KEY_DISABLED) { |
| 18202 | + info.trans_enabled = false; |
| 18203 | + omapfb_color_keys[mgr->id] = *ck; |
| 18204 | + |
| 18205 | + r = mgr->set_manager_info(mgr, &info); |
| 18206 | + if (r) |
| 18207 | + return r; |
| 18208 | + |
| 18209 | + r = mgr->apply(mgr); |
| 18210 | + |
| 18211 | + return r; |
| 18212 | + } |
| 18213 | + |
| 18214 | + switch (ck->key_type) { |
| 18215 | + case OMAPFB_COLOR_KEY_GFX_DST: |
| 18216 | + kt = OMAP_DSS_COLOR_KEY_GFX_DST; |
| 18217 | + break; |
| 18218 | + case OMAPFB_COLOR_KEY_VID_SRC: |
| 18219 | + kt = OMAP_DSS_COLOR_KEY_VID_SRC; |
| 18220 | + break; |
| 18221 | + default: |
| 18222 | + return -EINVAL; |
| 18223 | + } |
| 18224 | + |
| 18225 | + info.default_color = ck->background; |
| 18226 | + info.trans_key = ck->trans_key; |
| 18227 | + info.trans_key_type = kt; |
| 18228 | + info.trans_enabled = true; |
| 18229 | + |
| 18230 | + omapfb_color_keys[mgr->id] = *ck; |
| 18231 | + |
| 18232 | + r = mgr->set_manager_info(mgr, &info); |
| 18233 | + if (r) |
| 18234 | + return r; |
| 18235 | + |
| 18236 | + r = mgr->apply(mgr); |
| 18237 | + |
| 18238 | + return r; |
| 18239 | +} |
| 18240 | + |
| 18241 | +static int omapfb_set_color_key(struct fb_info *fbi, |
| 18242 | + struct omapfb_color_key *ck) |
| 18243 | +{ |
| 18244 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 18245 | + struct omapfb2_device *fbdev = ofbi->fbdev; |
| 18246 | + int r; |
| 18247 | + int i; |
| 18248 | + struct omap_overlay_manager *mgr = NULL; |
| 18249 | + |
| 18250 | + omapfb_lock(fbdev); |
| 18251 | + |
| 18252 | + for (i = 0; i < ofbi->num_overlays; i++) { |
| 18253 | + if (ofbi->overlays[i]->manager) { |
| 18254 | + mgr = ofbi->overlays[i]->manager; |
| 18255 | + break; |
| 18256 | + } |
| 18257 | + } |
| 18258 | + |
| 18259 | + if (!mgr) { |
| 18260 | + r = -EINVAL; |
| 18261 | + goto err; |
| 18262 | + } |
| 18263 | + |
| 18264 | + r = _omapfb_set_color_key(mgr, ck); |
| 18265 | +err: |
| 18266 | + omapfb_unlock(fbdev); |
| 18267 | + |
| 18268 | + return r; |
| 18269 | +} |
| 18270 | + |
| 18271 | +static int omapfb_get_color_key(struct fb_info *fbi, |
| 18272 | + struct omapfb_color_key *ck) |
| 18273 | +{ |
| 18274 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 18275 | + struct omapfb2_device *fbdev = ofbi->fbdev; |
| 18276 | + struct omap_overlay_manager *mgr = NULL; |
| 18277 | + int r = 0; |
| 18278 | + int i; |
| 18279 | + |
| 18280 | + omapfb_lock(fbdev); |
| 18281 | + |
| 18282 | + for (i = 0; i < ofbi->num_overlays; i++) { |
| 18283 | + if (ofbi->overlays[i]->manager) { |
| 18284 | + mgr = ofbi->overlays[i]->manager; |
| 18285 | + break; |
| 18286 | + } |
| 18287 | + } |
| 18288 | + |
| 18289 | + if (!mgr) { |
| 18290 | + r = -EINVAL; |
| 18291 | + goto err; |
| 18292 | + } |
| 18293 | + |
| 18294 | + *ck = omapfb_color_keys[mgr->id]; |
| 18295 | +err: |
| 18296 | + omapfb_unlock(fbdev); |
| 18297 | + |
| 18298 | + return r; |
| 18299 | +} |
| 18300 | + |
| 18301 | +static int omapfb_memory_read(struct fb_info *fbi, |
| 18302 | + struct omapfb_memory_read *mr) |
| 18303 | +{ |
| 18304 | + struct omap_dss_device *display = fb2display(fbi); |
| 18305 | + void *buf; |
| 18306 | + int r; |
| 18307 | + |
| 18308 | + if (!display || !display->memory_read) |
| 18309 | + return -ENOENT; |
| 18310 | + |
| 18311 | + if (!access_ok(VERIFY_WRITE, mr->buffer, mr->buffer_size)) |
| 18312 | + return -EFAULT; |
| 18313 | + |
| 18314 | + if (mr->w * mr->h * 3 > mr->buffer_size) |
| 18315 | + return -EINVAL; |
| 18316 | + |
| 18317 | + buf = vmalloc(mr->buffer_size); |
| 18318 | + if (!buf) { |
| 18319 | + DBG("vmalloc failed\n"); |
| 18320 | + return -ENOMEM; |
| 18321 | + } |
| 18322 | + |
| 18323 | + r = display->memory_read(display, buf, mr->buffer_size, |
| 18324 | + mr->x, mr->y, mr->w, mr->h); |
| 18325 | + |
| 18326 | + if (r > 0) { |
| 18327 | + if (copy_to_user(mr->buffer, buf, mr->buffer_size)) |
| 18328 | + r = -EFAULT; |
| 18329 | + } |
| 18330 | + |
| 18331 | + vfree(buf); |
| 18332 | + |
| 18333 | + return r; |
| 18334 | +} |
| 18335 | + |
| 18336 | +static int omapfb_get_ovl_colormode(struct omapfb2_device *fbdev, |
| 18337 | + struct omapfb_ovl_colormode *mode) |
| 18338 | +{ |
| 18339 | + int ovl_idx = mode->overlay_idx; |
| 18340 | + int mode_idx = mode->mode_idx; |
| 18341 | + struct omap_overlay *ovl; |
| 18342 | + enum omap_color_mode supported_modes; |
| 18343 | + struct fb_var_screeninfo var; |
| 18344 | + int i; |
| 18345 | + |
| 18346 | + if (ovl_idx >= fbdev->num_overlays) |
| 18347 | + return -ENODEV; |
| 18348 | + ovl = fbdev->overlays[ovl_idx]; |
| 18349 | + supported_modes = ovl->supported_modes; |
| 18350 | + |
| 18351 | + mode_idx = mode->mode_idx; |
| 18352 | + |
| 18353 | + for (i = 0; i < sizeof(supported_modes) * 8; i++) { |
| 18354 | + if (!(supported_modes & (1 << i))) |
| 18355 | + continue; |
| 18356 | + /* |
| 18357 | + * It's possible that the FB doesn't support a mode |
| 18358 | + * that is supported by the overlay, so call the |
| 18359 | + * following here. |
| 18360 | + */ |
| 18361 | + if (dss_mode_to_fb_mode(1 << i, &var) < 0) |
| 18362 | + continue; |
| 18363 | + |
| 18364 | + mode_idx--; |
| 18365 | + if (mode_idx < 0) |
| 18366 | + break; |
| 18367 | + } |
| 18368 | + |
| 18369 | + if (i == sizeof(supported_modes) * 8) |
| 18370 | + return -ENOENT; |
| 18371 | + |
| 18372 | + mode->bits_per_pixel = var.bits_per_pixel; |
| 18373 | + mode->nonstd = var.nonstd; |
| 18374 | + mode->red = var.red; |
| 18375 | + mode->green = var.green; |
| 18376 | + mode->blue = var.blue; |
| 18377 | + mode->transp = var.transp; |
| 18378 | + |
| 18379 | + return 0; |
| 18380 | +} |
| 18381 | + |
| 18382 | +static int omapfb_wait_for_go(struct fb_info *fbi) |
| 18383 | +{ |
| 18384 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 18385 | + int r = 0; |
| 18386 | + int i; |
| 18387 | + |
| 18388 | + for (i = 0; i < ofbi->num_overlays; ++i) { |
| 18389 | + struct omap_overlay *ovl = ofbi->overlays[i]; |
| 18390 | + r = ovl->wait_for_go(ovl); |
| 18391 | + if (r) |
| 18392 | + break; |
| 18393 | + } |
| 18394 | + |
| 18395 | + return r; |
| 18396 | +} |
| 18397 | + |
| 18398 | +int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg) |
| 18399 | +{ |
| 18400 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 18401 | + struct omapfb2_device *fbdev = ofbi->fbdev; |
| 18402 | + struct omap_dss_device *display = fb2display(fbi); |
| 18403 | + |
| 18404 | + union { |
| 18405 | + struct omapfb_update_window_old uwnd_o; |
| 18406 | + struct omapfb_update_window uwnd; |
| 18407 | + struct omapfb_plane_info plane_info; |
| 18408 | + struct omapfb_caps caps; |
| 18409 | + struct omapfb_mem_info mem_info; |
| 18410 | + struct omapfb_color_key color_key; |
| 18411 | + struct omapfb_ovl_colormode ovl_colormode; |
| 18412 | + enum omapfb_update_mode update_mode; |
| 18413 | + int test_num; |
| 18414 | + struct omapfb_memory_read memory_read; |
| 18415 | + struct omapfb_vram_info vram_info; |
| 18416 | + } p; |
| 18417 | + |
| 18418 | + int r = 0; |
| 18419 | + |
| 18420 | + switch (cmd) { |
| 18421 | + case OMAPFB_SYNC_GFX: |
| 18422 | + DBG("ioctl SYNC_GFX\n"); |
| 18423 | + if (!display || !display->sync) { |
| 18424 | + /* DSS1 never returns an error here, so we neither */ |
| 18425 | + /*r = -EINVAL;*/ |
| 18426 | + break; |
| 18427 | + } |
| 18428 | + |
| 18429 | + r = display->sync(display); |
| 18430 | + break; |
| 18431 | + |
| 18432 | + case OMAPFB_UPDATE_WINDOW_OLD: |
| 18433 | + DBG("ioctl UPDATE_WINDOW_OLD\n"); |
| 18434 | + if (!display || !display->update) { |
| 18435 | + r = -EINVAL; |
| 18436 | + break; |
| 18437 | + } |
| 18438 | + |
| 18439 | + if (copy_from_user(&p.uwnd_o, |
| 18440 | + (void __user *)arg, |
| 18441 | + sizeof(p.uwnd_o))) { |
| 18442 | + r = -EFAULT; |
| 18443 | + break; |
| 18444 | + } |
| 18445 | + |
| 18446 | + r = omapfb_update_window(fbi, p.uwnd_o.x, p.uwnd_o.y, |
| 18447 | + p.uwnd_o.width, p.uwnd_o.height); |
| 18448 | + break; |
| 18449 | + |
| 18450 | + case OMAPFB_UPDATE_WINDOW: |
| 18451 | + DBG("ioctl UPDATE_WINDOW\n"); |
| 18452 | + if (!display || !display->update) { |
| 18453 | + r = -EINVAL; |
| 18454 | + break; |
| 18455 | + } |
| 18456 | + |
| 18457 | + if (copy_from_user(&p.uwnd, (void __user *)arg, |
| 18458 | + sizeof(p.uwnd))) { |
| 18459 | + r = -EFAULT; |
| 18460 | + break; |
| 18461 | + } |
| 18462 | + |
| 18463 | + r = omapfb_update_window(fbi, p.uwnd.x, p.uwnd.y, |
| 18464 | + p.uwnd.width, p.uwnd.height); |
| 18465 | + break; |
| 18466 | + |
| 18467 | + case OMAPFB_SETUP_PLANE: |
| 18468 | + DBG("ioctl SETUP_PLANE\n"); |
| 18469 | + if (copy_from_user(&p.plane_info, (void __user *)arg, |
| 18470 | + sizeof(p.plane_info))) |
| 18471 | + r = -EFAULT; |
| 18472 | + else |
| 18473 | + r = omapfb_setup_plane(fbi, &p.plane_info); |
| 18474 | + break; |
| 18475 | + |
| 18476 | + case OMAPFB_QUERY_PLANE: |
| 18477 | + DBG("ioctl QUERY_PLANE\n"); |
| 18478 | + r = omapfb_query_plane(fbi, &p.plane_info); |
| 18479 | + if (r < 0) |
| 18480 | + break; |
| 18481 | + if (copy_to_user((void __user *)arg, &p.plane_info, |
| 18482 | + sizeof(p.plane_info))) |
| 18483 | + r = -EFAULT; |
| 18484 | + break; |
| 18485 | + |
| 18486 | + case OMAPFB_SETUP_MEM: |
| 18487 | + DBG("ioctl SETUP_MEM\n"); |
| 18488 | + if (copy_from_user(&p.mem_info, (void __user *)arg, |
| 18489 | + sizeof(p.mem_info))) |
| 18490 | + r = -EFAULT; |
| 18491 | + else |
| 18492 | + r = omapfb_setup_mem(fbi, &p.mem_info); |
| 18493 | + break; |
| 18494 | + |
| 18495 | + case OMAPFB_QUERY_MEM: |
| 18496 | + DBG("ioctl QUERY_MEM\n"); |
| 18497 | + r = omapfb_query_mem(fbi, &p.mem_info); |
| 18498 | + if (r < 0) |
| 18499 | + break; |
| 18500 | + if (copy_to_user((void __user *)arg, &p.mem_info, |
| 18501 | + sizeof(p.mem_info))) |
| 18502 | + r = -EFAULT; |
| 18503 | + break; |
| 18504 | + |
| 18505 | + case OMAPFB_GET_CAPS: |
| 18506 | + DBG("ioctl GET_CAPS\n"); |
| 18507 | + if (!display) { |
| 18508 | + r = -EINVAL; |
| 18509 | + break; |
| 18510 | + } |
| 18511 | + |
| 18512 | + memset(&p.caps, 0, sizeof(p.caps)); |
| 18513 | + p.caps.ctrl = display->caps; |
| 18514 | + |
| 18515 | + if (copy_to_user((void __user *)arg, &p.caps, sizeof(p.caps))) |
| 18516 | + r = -EFAULT; |
| 18517 | + break; |
| 18518 | + |
| 18519 | + case OMAPFB_GET_OVERLAY_COLORMODE: |
| 18520 | + DBG("ioctl GET_OVERLAY_COLORMODE\n"); |
| 18521 | + if (copy_from_user(&p.ovl_colormode, (void __user *)arg, |
| 18522 | + sizeof(p.ovl_colormode))) { |
| 18523 | + r = -EFAULT; |
| 18524 | + break; |
| 18525 | + } |
| 18526 | + r = omapfb_get_ovl_colormode(fbdev, &p.ovl_colormode); |
| 18527 | + if (r < 0) |
| 18528 | + break; |
| 18529 | + if (copy_to_user((void __user *)arg, &p.ovl_colormode, |
| 18530 | + sizeof(p.ovl_colormode))) |
| 18531 | + r = -EFAULT; |
| 18532 | + break; |
| 18533 | + |
| 18534 | + case OMAPFB_SET_UPDATE_MODE: |
| 18535 | + DBG("ioctl SET_UPDATE_MODE\n"); |
| 18536 | + if (get_user(p.update_mode, (int __user *)arg)) |
| 18537 | + r = -EFAULT; |
| 18538 | + else |
| 18539 | + r = omapfb_set_update_mode(fbi, p.update_mode); |
| 18540 | + break; |
| 18541 | + |
| 18542 | + case OMAPFB_GET_UPDATE_MODE: |
| 18543 | + DBG("ioctl GET_UPDATE_MODE\n"); |
| 18544 | + r = omapfb_get_update_mode(fbi, &p.update_mode); |
| 18545 | + if (r) |
| 18546 | + break; |
| 18547 | + if (put_user(p.update_mode, |
| 18548 | + (enum omapfb_update_mode __user *)arg)) |
| 18549 | + r = -EFAULT; |
| 18550 | + break; |
| 18551 | + |
| 18552 | + case OMAPFB_SET_COLOR_KEY: |
| 18553 | + DBG("ioctl SET_COLOR_KEY\n"); |
| 18554 | + if (copy_from_user(&p.color_key, (void __user *)arg, |
| 18555 | + sizeof(p.color_key))) |
| 18556 | + r = -EFAULT; |
| 18557 | + else |
| 18558 | + r = omapfb_set_color_key(fbi, &p.color_key); |
| 18559 | + break; |
| 18560 | + |
| 18561 | + case OMAPFB_GET_COLOR_KEY: |
| 18562 | + DBG("ioctl GET_COLOR_KEY\n"); |
| 18563 | + r = omapfb_get_color_key(fbi, &p.color_key); |
| 18564 | + if (r) |
| 18565 | + break; |
| 18566 | + if (copy_to_user((void __user *)arg, &p.color_key, |
| 18567 | + sizeof(p.color_key))) |
| 18568 | + r = -EFAULT; |
| 18569 | + break; |
| 18570 | + |
| 18571 | + case OMAPFB_WAITFORVSYNC: |
| 18572 | + DBG("ioctl WAITFORVSYNC\n"); |
| 18573 | + if (!display) { |
| 18574 | + r = -EINVAL; |
| 18575 | + break; |
| 18576 | + } |
| 18577 | + |
| 18578 | + r = display->wait_vsync(display); |
| 18579 | + break; |
| 18580 | + |
| 18581 | + case OMAPFB_WAITFORGO: |
| 18582 | + DBG("ioctl WAITFORGO\n"); |
| 18583 | + if (!display) { |
| 18584 | + r = -EINVAL; |
| 18585 | + break; |
| 18586 | + } |
| 18587 | + |
| 18588 | + r = omapfb_wait_for_go(fbi); |
| 18589 | + break; |
| 18590 | + |
| 18591 | + /* LCD and CTRL tests do the same thing for backward |
| 18592 | + * compatibility */ |
| 18593 | + case OMAPFB_LCD_TEST: |
| 18594 | + DBG("ioctl LCD_TEST\n"); |
| 18595 | + if (get_user(p.test_num, (int __user *)arg)) { |
| 18596 | + r = -EFAULT; |
| 18597 | + break; |
| 18598 | + } |
| 18599 | + if (!display || !display->run_test) { |
| 18600 | + r = -EINVAL; |
| 18601 | + break; |
| 18602 | + } |
| 18603 | + |
| 18604 | + r = display->run_test(display, p.test_num); |
| 18605 | + |
| 18606 | + break; |
| 18607 | + |
| 18608 | + case OMAPFB_CTRL_TEST: |
| 18609 | + DBG("ioctl CTRL_TEST\n"); |
| 18610 | + if (get_user(p.test_num, (int __user *)arg)) { |
| 18611 | + r = -EFAULT; |
| 18612 | + break; |
| 18613 | + } |
| 18614 | + if (!display || !display->run_test) { |
| 18615 | + r = -EINVAL; |
| 18616 | + break; |
| 18617 | + } |
| 18618 | + |
| 18619 | + r = display->run_test(display, p.test_num); |
| 18620 | + |
| 18621 | + break; |
| 18622 | + |
| 18623 | + case OMAPFB_MEMORY_READ: |
| 18624 | + DBG("ioctl MEMORY_READ\n"); |
| 18625 | + |
| 18626 | + if (copy_from_user(&p.memory_read, (void __user *)arg, |
| 18627 | + sizeof(p.memory_read))) { |
| 18628 | + r = -EFAULT; |
| 18629 | + break; |
| 18630 | + } |
| 18631 | + |
| 18632 | + r = omapfb_memory_read(fbi, &p.memory_read); |
| 18633 | + |
| 18634 | + break; |
| 18635 | + |
| 18636 | + case OMAPFB_GET_VRAM_INFO: { |
| 18637 | + unsigned long vram, free, largest; |
| 18638 | + |
| 18639 | + DBG("ioctl GET_VRAM_INFO\n"); |
| 18640 | + |
| 18641 | + omap_vram_get_info(&vram, &free, &largest); |
| 18642 | + p.vram_info.total = vram; |
| 18643 | + p.vram_info.free = free; |
| 18644 | + p.vram_info.largest_free_block = largest; |
| 18645 | + |
| 18646 | + if (copy_to_user((void __user *)arg, &p.vram_info, |
| 18647 | + sizeof(p.vram_info))) |
| 18648 | + r = -EFAULT; |
| 18649 | + break; |
| 18650 | + } |
| 18651 | + |
| 18652 | + default: |
| 18653 | + dev_err(fbdev->dev, "Unknown ioctl 0x%x\n", cmd); |
| 18654 | + r = -EINVAL; |
| 18655 | + } |
| 18656 | + |
| 18657 | + if (r < 0) |
| 18658 | + DBG("ioctl failed: %d\n", r); |
| 18659 | + |
| 18660 | + return r; |
| 18661 | +} |
| 18662 | + |
| 18663 | + |
| 18664 | --- /dev/null |
| 18665 | +++ b/drivers/video/omap2/omapfb/omapfb-main.c |
| 18666 | @@ -0,0 +1,2137 @@ |
| 18667 | +/* |
| 18668 | + * linux/drivers/video/omap2/omapfb-main.c |
| 18669 | + * |
| 18670 | + * Copyright (C) 2008 Nokia Corporation |
| 18671 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 18672 | + * |
| 18673 | + * Some code and ideas taken from drivers/video/omap/ driver |
| 18674 | + * by Imre Deak. |
| 18675 | + * |
| 18676 | + * This program is free software; you can redistribute it and/or modify it |
| 18677 | + * under the terms of the GNU General Public License version 2 as published by |
| 18678 | + * the Free Software Foundation. |
| 18679 | + * |
| 18680 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 18681 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 18682 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 18683 | + * more details. |
| 18684 | + * |
| 18685 | + * You should have received a copy of the GNU General Public License along with |
| 18686 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 18687 | + */ |
| 18688 | + |
| 18689 | +#include <linux/module.h> |
| 18690 | +#include <linux/delay.h> |
| 18691 | +#include <linux/fb.h> |
| 18692 | +#include <linux/dma-mapping.h> |
| 18693 | +#include <linux/vmalloc.h> |
| 18694 | +#include <linux/device.h> |
| 18695 | +#include <linux/platform_device.h> |
| 18696 | +#include <linux/omapfb.h> |
| 18697 | + |
| 18698 | +#include <mach/display.h> |
| 18699 | +#include <mach/vram.h> |
| 18700 | +#include <mach/vrfb.h> |
| 18701 | + |
| 18702 | +#include "omapfb.h" |
| 18703 | + |
| 18704 | +#define MODULE_NAME "omapfb" |
| 18705 | + |
| 18706 | +#define OMAPFB_PLANE_XRES_MIN 8 |
| 18707 | +#define OMAPFB_PLANE_YRES_MIN 8 |
| 18708 | + |
| 18709 | +static char *def_mode; |
| 18710 | +static char *def_vram; |
| 18711 | +static int def_vrfb; |
| 18712 | +static int def_rotate; |
| 18713 | +static int def_mirror; |
| 18714 | + |
| 18715 | +#ifdef DEBUG |
| 18716 | +unsigned int omapfb_debug; |
| 18717 | +module_param_named(debug, omapfb_debug, bool, 0644); |
| 18718 | +static unsigned int omapfb_test_pattern; |
| 18719 | +module_param_named(test, omapfb_test_pattern, bool, 0644); |
| 18720 | +#endif |
| 18721 | + |
| 18722 | +static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi); |
| 18723 | + |
| 18724 | +#ifdef DEBUG |
| 18725 | +static void draw_pixel(struct fb_info *fbi, int x, int y, unsigned color) |
| 18726 | +{ |
| 18727 | + struct fb_var_screeninfo *var = &fbi->var; |
| 18728 | + struct fb_fix_screeninfo *fix = &fbi->fix; |
| 18729 | + void __iomem *addr = fbi->screen_base; |
| 18730 | + const unsigned bytespp = var->bits_per_pixel >> 3; |
| 18731 | + const unsigned line_len = fix->line_length / bytespp; |
| 18732 | + |
| 18733 | + int r = (color >> 16) & 0xff; |
| 18734 | + int g = (color >> 8) & 0xff; |
| 18735 | + int b = (color >> 0) & 0xff; |
| 18736 | + |
| 18737 | + if (var->bits_per_pixel == 16) { |
| 18738 | + u16 __iomem *p = (u16 __iomem *)addr; |
| 18739 | + p += y * line_len + x; |
| 18740 | + |
| 18741 | + r = r * 32 / 256; |
| 18742 | + g = g * 64 / 256; |
| 18743 | + b = b * 32 / 256; |
| 18744 | + |
| 18745 | + __raw_writew((r << 11) | (g << 5) | (b << 0), p); |
| 18746 | + } else if (var->bits_per_pixel == 24) { |
| 18747 | + u8 __iomem *p = (u8 __iomem *)addr; |
| 18748 | + p += (y * line_len + x) * 3; |
| 18749 | + |
| 18750 | + __raw_writeb(b, p + 0); |
| 18751 | + __raw_writeb(g, p + 1); |
| 18752 | + __raw_writeb(r, p + 2); |
| 18753 | + } else if (var->bits_per_pixel == 32) { |
| 18754 | + u32 __iomem *p = (u32 __iomem *)addr; |
| 18755 | + p += y * line_len + x; |
| 18756 | + __raw_writel(color, p); |
| 18757 | + } |
| 18758 | +} |
| 18759 | + |
| 18760 | +static void fill_fb(struct fb_info *fbi) |
| 18761 | +{ |
| 18762 | + struct fb_var_screeninfo *var = &fbi->var; |
| 18763 | + const short w = var->xres_virtual; |
| 18764 | + const short h = var->yres_virtual; |
| 18765 | + void __iomem *addr = fbi->screen_base; |
| 18766 | + int y, x; |
| 18767 | + |
| 18768 | + if (!addr) |
| 18769 | + return; |
| 18770 | + |
| 18771 | + DBG("fill_fb %dx%d, line_len %d bytes\n", w, h, fbi->fix.line_length); |
| 18772 | + |
| 18773 | + for (y = 0; y < h; y++) { |
| 18774 | + for (x = 0; x < w; x++) { |
| 18775 | + if (x < 20 && y < 20) |
| 18776 | + draw_pixel(fbi, x, y, 0xffffff); |
| 18777 | + else if (x < 20 && (y > 20 && y < h - 20)) |
| 18778 | + draw_pixel(fbi, x, y, 0xff); |
| 18779 | + else if (y < 20 && (x > 20 && x < w - 20)) |
| 18780 | + draw_pixel(fbi, x, y, 0xff00); |
| 18781 | + else if (x > w - 20 && (y > 20 && y < h - 20)) |
| 18782 | + draw_pixel(fbi, x, y, 0xff0000); |
| 18783 | + else if (y > h - 20 && (x > 20 && x < w - 20)) |
| 18784 | + draw_pixel(fbi, x, y, 0xffff00); |
| 18785 | + else if (x == 20 || x == w - 20 || |
| 18786 | + y == 20 || y == h - 20) |
| 18787 | + draw_pixel(fbi, x, y, 0xffffff); |
| 18788 | + else if (x == y || w - x == h - y) |
| 18789 | + draw_pixel(fbi, x, y, 0xff00ff); |
| 18790 | + else if (w - x == y || x == h - y) |
| 18791 | + draw_pixel(fbi, x, y, 0x00ffff); |
| 18792 | + else if (x > 20 && y > 20 && x < w - 20 && y < h - 20) { |
| 18793 | + int t = x * 3 / w; |
| 18794 | + unsigned r = 0, g = 0, b = 0; |
| 18795 | + unsigned c; |
| 18796 | + if (var->bits_per_pixel == 16) { |
| 18797 | + if (t == 0) |
| 18798 | + b = (y % 32) * 256 / 32; |
| 18799 | + else if (t == 1) |
| 18800 | + g = (y % 64) * 256 / 64; |
| 18801 | + else if (t == 2) |
| 18802 | + r = (y % 32) * 256 / 32; |
| 18803 | + } else { |
| 18804 | + if (t == 0) |
| 18805 | + b = (y % 256); |
| 18806 | + else if (t == 1) |
| 18807 | + g = (y % 256); |
| 18808 | + else if (t == 2) |
| 18809 | + r = (y % 256); |
| 18810 | + } |
| 18811 | + c = (r << 16) | (g << 8) | (b << 0); |
| 18812 | + draw_pixel(fbi, x, y, c); |
| 18813 | + } else { |
| 18814 | + draw_pixel(fbi, x, y, 0); |
| 18815 | + } |
| 18816 | + } |
| 18817 | + } |
| 18818 | +} |
| 18819 | +#endif |
| 18820 | + |
| 18821 | +static unsigned omapfb_get_vrfb_offset(struct omapfb_info *ofbi, int rot) |
| 18822 | +{ |
| 18823 | + struct vrfb *vrfb = &ofbi->region.vrfb; |
| 18824 | + unsigned offset; |
| 18825 | + |
| 18826 | + switch (rot) { |
| 18827 | + case FB_ROTATE_UR: |
| 18828 | + offset = 0; |
| 18829 | + break; |
| 18830 | + case FB_ROTATE_CW: |
| 18831 | + offset = vrfb->yoffset; |
| 18832 | + break; |
| 18833 | + case FB_ROTATE_UD: |
| 18834 | + offset = vrfb->yoffset * OMAP_VRFB_LINE_LEN + vrfb->xoffset; |
| 18835 | + break; |
| 18836 | + case FB_ROTATE_CCW: |
| 18837 | + offset = vrfb->xoffset * OMAP_VRFB_LINE_LEN; |
| 18838 | + break; |
| 18839 | + default: |
| 18840 | + BUG(); |
| 18841 | + } |
| 18842 | + |
| 18843 | + offset *= vrfb->bytespp; |
| 18844 | + |
| 18845 | + return offset; |
| 18846 | +} |
| 18847 | + |
| 18848 | +static u32 omapfb_get_region_rot_paddr(struct omapfb_info *ofbi, int rot) |
| 18849 | +{ |
| 18850 | + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { |
| 18851 | + return ofbi->region.vrfb.paddr[rot] |
| 18852 | + + omapfb_get_vrfb_offset(ofbi, rot); |
| 18853 | + } else { |
| 18854 | + return ofbi->region.paddr; |
| 18855 | + } |
| 18856 | +} |
| 18857 | + |
| 18858 | +static u32 omapfb_get_region_paddr(struct omapfb_info *ofbi) |
| 18859 | +{ |
| 18860 | + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) |
| 18861 | + return ofbi->region.vrfb.paddr[0]; |
| 18862 | + else |
| 18863 | + return ofbi->region.paddr; |
| 18864 | +} |
| 18865 | + |
| 18866 | +static void __iomem *omapfb_get_region_vaddr(struct omapfb_info *ofbi) |
| 18867 | +{ |
| 18868 | + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) |
| 18869 | + return ofbi->region.vrfb.vaddr[0]; |
| 18870 | + else |
| 18871 | + return ofbi->region.vaddr; |
| 18872 | +} |
| 18873 | + |
| 18874 | +static struct omapfb_colormode omapfb_colormodes[] = { |
| 18875 | + { |
| 18876 | + .dssmode = OMAP_DSS_COLOR_UYVY, |
| 18877 | + .bits_per_pixel = 16, |
| 18878 | + .nonstd = OMAPFB_COLOR_YUV422, |
| 18879 | + }, { |
| 18880 | + .dssmode = OMAP_DSS_COLOR_YUV2, |
| 18881 | + .bits_per_pixel = 16, |
| 18882 | + .nonstd = OMAPFB_COLOR_YUY422, |
| 18883 | + }, { |
| 18884 | + .dssmode = OMAP_DSS_COLOR_ARGB16, |
| 18885 | + .bits_per_pixel = 16, |
| 18886 | + .red = { .length = 4, .offset = 8, .msb_right = 0 }, |
| 18887 | + .green = { .length = 4, .offset = 4, .msb_right = 0 }, |
| 18888 | + .blue = { .length = 4, .offset = 0, .msb_right = 0 }, |
| 18889 | + .transp = { .length = 4, .offset = 12, .msb_right = 0 }, |
| 18890 | + }, { |
| 18891 | + .dssmode = OMAP_DSS_COLOR_RGB16, |
| 18892 | + .bits_per_pixel = 16, |
| 18893 | + .red = { .length = 5, .offset = 11, .msb_right = 0 }, |
| 18894 | + .green = { .length = 6, .offset = 5, .msb_right = 0 }, |
| 18895 | + .blue = { .length = 5, .offset = 0, .msb_right = 0 }, |
| 18896 | + .transp = { .length = 0, .offset = 0, .msb_right = 0 }, |
| 18897 | + }, { |
| 18898 | + .dssmode = OMAP_DSS_COLOR_RGB24P, |
| 18899 | + .bits_per_pixel = 24, |
| 18900 | + .red = { .length = 8, .offset = 16, .msb_right = 0 }, |
| 18901 | + .green = { .length = 8, .offset = 8, .msb_right = 0 }, |
| 18902 | + .blue = { .length = 8, .offset = 0, .msb_right = 0 }, |
| 18903 | + .transp = { .length = 0, .offset = 0, .msb_right = 0 }, |
| 18904 | + }, { |
| 18905 | + .dssmode = OMAP_DSS_COLOR_RGB24U, |
| 18906 | + .bits_per_pixel = 32, |
| 18907 | + .red = { .length = 8, .offset = 16, .msb_right = 0 }, |
| 18908 | + .green = { .length = 8, .offset = 8, .msb_right = 0 }, |
| 18909 | + .blue = { .length = 8, .offset = 0, .msb_right = 0 }, |
| 18910 | + .transp = { .length = 0, .offset = 0, .msb_right = 0 }, |
| 18911 | + }, { |
| 18912 | + .dssmode = OMAP_DSS_COLOR_ARGB32, |
| 18913 | + .bits_per_pixel = 32, |
| 18914 | + .red = { .length = 8, .offset = 16, .msb_right = 0 }, |
| 18915 | + .green = { .length = 8, .offset = 8, .msb_right = 0 }, |
| 18916 | + .blue = { .length = 8, .offset = 0, .msb_right = 0 }, |
| 18917 | + .transp = { .length = 8, .offset = 24, .msb_right = 0 }, |
| 18918 | + }, { |
| 18919 | + .dssmode = OMAP_DSS_COLOR_RGBA32, |
| 18920 | + .bits_per_pixel = 32, |
| 18921 | + .red = { .length = 8, .offset = 24, .msb_right = 0 }, |
| 18922 | + .green = { .length = 8, .offset = 16, .msb_right = 0 }, |
| 18923 | + .blue = { .length = 8, .offset = 8, .msb_right = 0 }, |
| 18924 | + .transp = { .length = 8, .offset = 0, .msb_right = 0 }, |
| 18925 | + }, { |
| 18926 | + .dssmode = OMAP_DSS_COLOR_RGBX32, |
| 18927 | + .bits_per_pixel = 32, |
| 18928 | + .red = { .length = 8, .offset = 24, .msb_right = 0 }, |
| 18929 | + .green = { .length = 8, .offset = 16, .msb_right = 0 }, |
| 18930 | + .blue = { .length = 8, .offset = 8, .msb_right = 0 }, |
| 18931 | + .transp = { .length = 0, .offset = 0, .msb_right = 0 }, |
| 18932 | + }, |
| 18933 | +}; |
| 18934 | + |
| 18935 | +static bool cmp_var_to_colormode(struct fb_var_screeninfo *var, |
| 18936 | + struct omapfb_colormode *color) |
| 18937 | +{ |
| 18938 | + bool cmp_component(struct fb_bitfield *f1, struct fb_bitfield *f2) |
| 18939 | + { |
| 18940 | + return f1->length == f2->length && |
| 18941 | + f1->offset == f2->offset && |
| 18942 | + f1->msb_right == f2->msb_right; |
| 18943 | + } |
| 18944 | + |
| 18945 | + if (var->bits_per_pixel == 0 || |
| 18946 | + var->red.length == 0 || |
| 18947 | + var->blue.length == 0 || |
| 18948 | + var->green.length == 0) |
| 18949 | + return 0; |
| 18950 | + |
| 18951 | + return var->bits_per_pixel == color->bits_per_pixel && |
| 18952 | + cmp_component(&var->red, &color->red) && |
| 18953 | + cmp_component(&var->green, &color->green) && |
| 18954 | + cmp_component(&var->blue, &color->blue) && |
| 18955 | + cmp_component(&var->transp, &color->transp); |
| 18956 | +} |
| 18957 | + |
| 18958 | +static void assign_colormode_to_var(struct fb_var_screeninfo *var, |
| 18959 | + struct omapfb_colormode *color) |
| 18960 | +{ |
| 18961 | + var->bits_per_pixel = color->bits_per_pixel; |
| 18962 | + var->nonstd = color->nonstd; |
| 18963 | + var->red = color->red; |
| 18964 | + var->green = color->green; |
| 18965 | + var->blue = color->blue; |
| 18966 | + var->transp = color->transp; |
| 18967 | +} |
| 18968 | + |
| 18969 | +static enum omap_color_mode fb_mode_to_dss_mode(struct fb_var_screeninfo *var) |
| 18970 | +{ |
| 18971 | + enum omap_color_mode dssmode; |
| 18972 | + int i; |
| 18973 | + |
| 18974 | + /* first match with nonstd field */ |
| 18975 | + if (var->nonstd) { |
| 18976 | + for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { |
| 18977 | + struct omapfb_colormode *mode = &omapfb_colormodes[i]; |
| 18978 | + if (var->nonstd == mode->nonstd) { |
| 18979 | + assign_colormode_to_var(var, mode); |
| 18980 | + return mode->dssmode; |
| 18981 | + } |
| 18982 | + } |
| 18983 | + |
| 18984 | + return -EINVAL; |
| 18985 | + } |
| 18986 | + |
| 18987 | + /* then try exact match of bpp and colors */ |
| 18988 | + for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { |
| 18989 | + struct omapfb_colormode *mode = &omapfb_colormodes[i]; |
| 18990 | + if (cmp_var_to_colormode(var, mode)) { |
| 18991 | + assign_colormode_to_var(var, mode); |
| 18992 | + return mode->dssmode; |
| 18993 | + } |
| 18994 | + } |
| 18995 | + |
| 18996 | + /* match with bpp if user has not filled color fields |
| 18997 | + * properly */ |
| 18998 | + switch (var->bits_per_pixel) { |
| 18999 | + case 1: |
| 19000 | + dssmode = OMAP_DSS_COLOR_CLUT1; |
| 19001 | + break; |
| 19002 | + case 2: |
| 19003 | + dssmode = OMAP_DSS_COLOR_CLUT2; |
| 19004 | + break; |
| 19005 | + case 4: |
| 19006 | + dssmode = OMAP_DSS_COLOR_CLUT4; |
| 19007 | + break; |
| 19008 | + case 8: |
| 19009 | + dssmode = OMAP_DSS_COLOR_CLUT8; |
| 19010 | + break; |
| 19011 | + case 12: |
| 19012 | + dssmode = OMAP_DSS_COLOR_RGB12U; |
| 19013 | + break; |
| 19014 | + case 16: |
| 19015 | + dssmode = OMAP_DSS_COLOR_RGB16; |
| 19016 | + break; |
| 19017 | + case 24: |
| 19018 | + dssmode = OMAP_DSS_COLOR_RGB24P; |
| 19019 | + break; |
| 19020 | + case 32: |
| 19021 | + dssmode = OMAP_DSS_COLOR_RGB24U; |
| 19022 | + break; |
| 19023 | + default: |
| 19024 | + return -EINVAL; |
| 19025 | + } |
| 19026 | + |
| 19027 | + for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { |
| 19028 | + struct omapfb_colormode *mode = &omapfb_colormodes[i]; |
| 19029 | + if (dssmode == mode->dssmode) { |
| 19030 | + assign_colormode_to_var(var, mode); |
| 19031 | + return mode->dssmode; |
| 19032 | + } |
| 19033 | + } |
| 19034 | + |
| 19035 | + return -EINVAL; |
| 19036 | +} |
| 19037 | + |
| 19038 | +int dss_mode_to_fb_mode(enum omap_color_mode dssmode, |
| 19039 | + struct fb_var_screeninfo *var) |
| 19040 | +{ |
| 19041 | + int i; |
| 19042 | + |
| 19043 | + for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { |
| 19044 | + struct omapfb_colormode *mode = &omapfb_colormodes[i]; |
| 19045 | + if (dssmode == mode->dssmode) { |
| 19046 | + assign_colormode_to_var(var, mode); |
| 19047 | + return 0; |
| 19048 | + } |
| 19049 | + } |
| 19050 | + return -ENOENT; |
| 19051 | +} |
| 19052 | + |
| 19053 | +void set_fb_fix(struct fb_info *fbi) |
| 19054 | +{ |
| 19055 | + struct fb_fix_screeninfo *fix = &fbi->fix; |
| 19056 | + struct fb_var_screeninfo *var = &fbi->var; |
| 19057 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 19058 | + struct omapfb2_mem_region *rg = &ofbi->region; |
| 19059 | + |
| 19060 | + DBG("set_fb_fix\n"); |
| 19061 | + |
| 19062 | + /* used by open/write in fbmem.c */ |
| 19063 | + fbi->screen_base = (char __iomem *)omapfb_get_region_vaddr(ofbi); |
| 19064 | + |
| 19065 | + DBG("changing rotation to %d\n", var->rotate); |
| 19066 | + |
| 19067 | + /* used by mmap in fbmem.c */ |
| 19068 | + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { |
| 19069 | + switch (var->nonstd) { |
| 19070 | + case OMAPFB_COLOR_YUV422: |
| 19071 | + case OMAPFB_COLOR_YUY422: |
| 19072 | + fix->line_length = |
| 19073 | + (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2; |
| 19074 | + break; |
| 19075 | + default: |
| 19076 | + fix->line_length = |
| 19077 | + (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3; |
| 19078 | + break; |
| 19079 | + } |
| 19080 | + } else |
| 19081 | + fix->line_length = |
| 19082 | + (var->xres_virtual * var->bits_per_pixel) >> 3; |
| 19083 | + fix->smem_start = omapfb_get_region_paddr(ofbi); |
| 19084 | + fix->smem_len = rg->size; |
| 19085 | + |
| 19086 | + fix->type = FB_TYPE_PACKED_PIXELS; |
| 19087 | + |
| 19088 | + if (var->nonstd) |
| 19089 | + fix->visual = FB_VISUAL_PSEUDOCOLOR; |
| 19090 | + else { |
| 19091 | + switch (var->bits_per_pixel) { |
| 19092 | + case 32: |
| 19093 | + case 24: |
| 19094 | + case 16: |
| 19095 | + case 12: |
| 19096 | + fix->visual = FB_VISUAL_TRUECOLOR; |
| 19097 | + /* 12bpp is stored in 16 bits */ |
| 19098 | + break; |
| 19099 | + case 1: |
| 19100 | + case 2: |
| 19101 | + case 4: |
| 19102 | + case 8: |
| 19103 | + fix->visual = FB_VISUAL_PSEUDOCOLOR; |
| 19104 | + break; |
| 19105 | + } |
| 19106 | + } |
| 19107 | + |
| 19108 | + fix->accel = FB_ACCEL_NONE; |
| 19109 | + |
| 19110 | + fix->xpanstep = 1; |
| 19111 | + fix->ypanstep = 1; |
| 19112 | + |
| 19113 | + if (rg->size && ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { |
| 19114 | + unsigned bytespp; |
| 19115 | + bool yuv_mode; |
| 19116 | + enum omap_color_mode mode; |
| 19117 | + |
| 19118 | + mode = fb_mode_to_dss_mode(var); |
| 19119 | + |
| 19120 | + bytespp = var->bits_per_pixel >> 3; |
| 19121 | + |
| 19122 | + if (mode == OMAP_DSS_COLOR_YUV2 || mode == OMAP_DSS_COLOR_UYVY) |
| 19123 | + yuv_mode = true; |
| 19124 | + else |
| 19125 | + yuv_mode = false; |
| 19126 | + |
| 19127 | + omap_vrfb_setup(&rg->vrfb, rg->paddr, |
| 19128 | + var->xres_virtual, |
| 19129 | + var->yres_virtual, |
| 19130 | + bytespp, yuv_mode); |
| 19131 | + } |
| 19132 | +} |
| 19133 | + |
| 19134 | +/* check new var and possibly modify it to be ok */ |
| 19135 | +int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var) |
| 19136 | +{ |
| 19137 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 19138 | + struct omap_dss_device *display = fb2display(fbi); |
| 19139 | + unsigned long max_frame_size; |
| 19140 | + unsigned long line_size; |
| 19141 | + int xres_min, yres_min; |
| 19142 | + int xres_max, yres_max; |
| 19143 | + enum omap_color_mode mode = 0; |
| 19144 | + int i; |
| 19145 | + int bytespp; |
| 19146 | + |
| 19147 | + DBG("check_fb_var %d\n", ofbi->id); |
| 19148 | + |
| 19149 | + if (ofbi->region.size == 0) |
| 19150 | + return 0; |
| 19151 | + |
| 19152 | + mode = fb_mode_to_dss_mode(var); |
| 19153 | + if (mode < 0) { |
| 19154 | + DBG("cannot convert var to omap dss mode\n"); |
| 19155 | + return -EINVAL; |
| 19156 | + } |
| 19157 | + |
| 19158 | + for (i = 0; i < ofbi->num_overlays; ++i) { |
| 19159 | + if ((ofbi->overlays[i]->supported_modes & mode) == 0) { |
| 19160 | + DBG("invalid mode\n"); |
| 19161 | + return -EINVAL; |
| 19162 | + } |
| 19163 | + } |
| 19164 | + |
| 19165 | + if (var->rotate < 0 || var->rotate > 3) |
| 19166 | + return -EINVAL; |
| 19167 | + |
| 19168 | + xres_min = OMAPFB_PLANE_XRES_MIN; |
| 19169 | + xres_max = 2048; |
| 19170 | + yres_min = OMAPFB_PLANE_YRES_MIN; |
| 19171 | + yres_max = 2048; |
| 19172 | + |
| 19173 | + bytespp = var->bits_per_pixel >> 3; |
| 19174 | + |
| 19175 | + /* XXX: some applications seem to set virtual res to 0. */ |
| 19176 | + if (var->xres_virtual == 0) |
| 19177 | + var->xres_virtual = var->xres; |
| 19178 | + |
| 19179 | + if (var->yres_virtual == 0) |
| 19180 | + var->yres_virtual = var->yres; |
| 19181 | + |
| 19182 | + if (var->xres_virtual < xres_min || var->yres_virtual < yres_min) |
| 19183 | + return -EINVAL; |
| 19184 | + |
| 19185 | + if (var->xres < xres_min) |
| 19186 | + var->xres = xres_min; |
| 19187 | + if (var->yres < yres_min) |
| 19188 | + var->yres = yres_min; |
| 19189 | + if (var->xres > xres_max) |
| 19190 | + var->xres = xres_max; |
| 19191 | + if (var->yres > yres_max) |
| 19192 | + var->yres = yres_max; |
| 19193 | + |
| 19194 | + if (var->xres > var->xres_virtual) |
| 19195 | + var->xres = var->xres_virtual; |
| 19196 | + if (var->yres > var->yres_virtual) |
| 19197 | + var->yres = var->yres_virtual; |
| 19198 | + |
| 19199 | + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) |
| 19200 | + line_size = OMAP_VRFB_LINE_LEN * bytespp; |
| 19201 | + else |
| 19202 | + line_size = var->xres_virtual * bytespp; |
| 19203 | + |
| 19204 | + max_frame_size = ofbi->region.size; |
| 19205 | + |
| 19206 | + DBG("max frame size %lu, line size %lu\n", max_frame_size, line_size); |
| 19207 | + |
| 19208 | + if (line_size * var->yres_virtual > max_frame_size) { |
| 19209 | + DBG("can't fit FB into memory, reducing y\n"); |
| 19210 | + var->yres_virtual = max_frame_size / line_size; |
| 19211 | + |
| 19212 | + if (var->yres_virtual < yres_min) |
| 19213 | + var->yres_virtual = yres_min; |
| 19214 | + |
| 19215 | + if (var->yres > var->yres_virtual) |
| 19216 | + var->yres = var->yres_virtual; |
| 19217 | + } |
| 19218 | + |
| 19219 | + if (line_size * var->yres_virtual > max_frame_size) { |
| 19220 | + DBG("can't fit FB into memory, reducing x\n"); |
| 19221 | + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) |
| 19222 | + return -EINVAL; |
| 19223 | + |
| 19224 | + var->xres_virtual = max_frame_size / var->yres_virtual / |
| 19225 | + bytespp; |
| 19226 | + |
| 19227 | + if (var->xres_virtual < xres_min) |
| 19228 | + var->xres_virtual = xres_min; |
| 19229 | + |
| 19230 | + if (var->xres > var->xres_virtual) |
| 19231 | + var->xres = var->xres_virtual; |
| 19232 | + |
| 19233 | + line_size = var->xres_virtual * bytespp; |
| 19234 | + } |
| 19235 | + |
| 19236 | + if (line_size * var->yres_virtual > max_frame_size) { |
| 19237 | + DBG("cannot fit FB to memory\n"); |
| 19238 | + return -EINVAL; |
| 19239 | + } |
| 19240 | + |
| 19241 | + if (var->xres + var->xoffset > var->xres_virtual) |
| 19242 | + var->xoffset = var->xres_virtual - var->xres; |
| 19243 | + if (var->yres + var->yoffset > var->yres_virtual) |
| 19244 | + var->yoffset = var->yres_virtual - var->yres; |
| 19245 | + |
| 19246 | + DBG("xres = %d, yres = %d, vxres = %d, vyres = %d\n", |
| 19247 | + var->xres, var->yres, |
| 19248 | + var->xres_virtual, var->yres_virtual); |
| 19249 | + |
| 19250 | + var->height = -1; |
| 19251 | + var->width = -1; |
| 19252 | + var->grayscale = 0; |
| 19253 | + |
| 19254 | + if (display && display->get_timings) { |
| 19255 | + struct omap_video_timings timings; |
| 19256 | + display->get_timings(display, &timings); |
| 19257 | + |
| 19258 | + /* pixclock in ps, the rest in pixclock */ |
| 19259 | + var->pixclock = timings.pixel_clock != 0 ? |
| 19260 | + KHZ2PICOS(timings.pixel_clock) : |
| 19261 | + 0; |
| 19262 | + var->left_margin = timings.hfp; |
| 19263 | + var->right_margin = timings.hbp; |
| 19264 | + var->upper_margin = timings.vfp; |
| 19265 | + var->lower_margin = timings.vbp; |
| 19266 | + var->hsync_len = timings.hsw; |
| 19267 | + var->vsync_len = timings.vsw; |
| 19268 | + } else { |
| 19269 | + var->pixclock = 0; |
| 19270 | + var->left_margin = 0; |
| 19271 | + var->right_margin = 0; |
| 19272 | + var->upper_margin = 0; |
| 19273 | + var->lower_margin = 0; |
| 19274 | + var->hsync_len = 0; |
| 19275 | + var->vsync_len = 0; |
| 19276 | + } |
| 19277 | + |
| 19278 | + /* TODO: get these from panel->config */ |
| 19279 | + var->vmode = FB_VMODE_NONINTERLACED; |
| 19280 | + var->sync = 0; |
| 19281 | + |
| 19282 | + return 0; |
| 19283 | +} |
| 19284 | + |
| 19285 | +/* |
| 19286 | + * --------------------------------------------------------------------------- |
| 19287 | + * fbdev framework callbacks |
| 19288 | + * --------------------------------------------------------------------------- |
| 19289 | + */ |
| 19290 | +static int omapfb_open(struct fb_info *fbi, int user) |
| 19291 | +{ |
| 19292 | + return 0; |
| 19293 | +} |
| 19294 | + |
| 19295 | +static int omapfb_release(struct fb_info *fbi, int user) |
| 19296 | +{ |
| 19297 | +#if 0 |
| 19298 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 19299 | + struct omapfb2_device *fbdev = ofbi->fbdev; |
| 19300 | + struct omap_dss_device *display = fb2display(fbi); |
| 19301 | + |
| 19302 | + DBG("Closing fb with plane index %d\n", ofbi->id); |
| 19303 | + |
| 19304 | + omapfb_lock(fbdev); |
| 19305 | + |
| 19306 | + if (display && display->get_update_mode && display->update) { |
| 19307 | + /* XXX this update should be removed, I think. But it's |
| 19308 | + * good for debugging */ |
| 19309 | + if (display->get_update_mode(display) == |
| 19310 | + OMAP_DSS_UPDATE_MANUAL) { |
| 19311 | + u16 w, h; |
| 19312 | + |
| 19313 | + if (display->sync) |
| 19314 | + display->sync(display); |
| 19315 | + |
| 19316 | + display->get_resolution(display, &w, &h); |
| 19317 | + display->update(display, 0, 0, w, h); |
| 19318 | + } |
| 19319 | + } |
| 19320 | + |
| 19321 | + if (display && display->sync) |
| 19322 | + display->sync(display); |
| 19323 | + |
| 19324 | + omapfb_unlock(fbdev); |
| 19325 | +#endif |
| 19326 | + return 0; |
| 19327 | +} |
| 19328 | + |
| 19329 | +/* setup overlay according to the fb */ |
| 19330 | +static int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, |
| 19331 | + u16 posx, u16 posy, u16 outw, u16 outh) |
| 19332 | +{ |
| 19333 | + int r = 0; |
| 19334 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 19335 | + struct fb_var_screeninfo *var = &fbi->var; |
| 19336 | + struct fb_fix_screeninfo *fix = &fbi->fix; |
| 19337 | + enum omap_color_mode mode = 0; |
| 19338 | + int offset; |
| 19339 | + u32 data_start_p; |
| 19340 | + void __iomem *data_start_v; |
| 19341 | + struct omap_overlay_info info; |
| 19342 | + int xres, yres; |
| 19343 | + int screen_width; |
| 19344 | + int mirror; |
| 19345 | + int rotation = var->rotate; |
| 19346 | + int i; |
| 19347 | + |
| 19348 | + for (i = 0; i < ofbi->num_overlays; i++) { |
| 19349 | + if (ovl != ofbi->overlays[i]) |
| 19350 | + continue; |
| 19351 | + |
| 19352 | + rotation = (rotation + ofbi->rotation[i]) % 4; |
| 19353 | + break; |
| 19354 | + } |
| 19355 | + |
| 19356 | + DBG("setup_overlay %d, posx %d, posy %d, outw %d, outh %d\n", ofbi->id, |
| 19357 | + posx, posy, outw, outh); |
| 19358 | + |
| 19359 | + if (rotation == FB_ROTATE_CW || rotation == FB_ROTATE_CCW) { |
| 19360 | + xres = var->yres; |
| 19361 | + yres = var->xres; |
| 19362 | + } else { |
| 19363 | + xres = var->xres; |
| 19364 | + yres = var->yres; |
| 19365 | + } |
| 19366 | + |
| 19367 | + offset = ((var->yoffset * var->xres_virtual + |
| 19368 | + var->xoffset) * var->bits_per_pixel) >> 3; |
| 19369 | + |
| 19370 | + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { |
| 19371 | + data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation); |
| 19372 | + data_start_v = NULL; |
| 19373 | + } else { |
| 19374 | + data_start_p = omapfb_get_region_paddr(ofbi); |
| 19375 | + data_start_v = omapfb_get_region_vaddr(ofbi); |
| 19376 | + } |
| 19377 | + |
| 19378 | + data_start_p += offset; |
| 19379 | + data_start_v += offset; |
| 19380 | + |
| 19381 | + mode = fb_mode_to_dss_mode(var); |
| 19382 | + |
| 19383 | + if (mode == -EINVAL) { |
| 19384 | + DBG("fb_mode_to_dss_mode failed"); |
| 19385 | + r = -EINVAL; |
| 19386 | + goto err; |
| 19387 | + } |
| 19388 | + |
| 19389 | + switch (var->nonstd) { |
| 19390 | + case OMAPFB_COLOR_YUV422: |
| 19391 | + case OMAPFB_COLOR_YUY422: |
| 19392 | + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { |
| 19393 | + screen_width = fix->line_length |
| 19394 | + / (var->bits_per_pixel >> 2); |
| 19395 | + break; |
| 19396 | + } |
| 19397 | + default: |
| 19398 | + screen_width = fix->line_length / (var->bits_per_pixel >> 3); |
| 19399 | + break; |
| 19400 | + } |
| 19401 | + |
| 19402 | + ovl->get_overlay_info(ovl, &info); |
| 19403 | + |
| 19404 | + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) |
| 19405 | + mirror = 0; |
| 19406 | + else |
| 19407 | + mirror = ofbi->mirror; |
| 19408 | + |
| 19409 | + info.paddr = data_start_p; |
| 19410 | + info.vaddr = data_start_v; |
| 19411 | + info.screen_width = screen_width; |
| 19412 | + info.width = xres; |
| 19413 | + info.height = yres; |
| 19414 | + info.color_mode = mode; |
| 19415 | + info.rotation_type = ofbi->rotation_type; |
| 19416 | + info.rotation = rotation; |
| 19417 | + info.mirror = mirror; |
| 19418 | + |
| 19419 | + info.pos_x = posx; |
| 19420 | + info.pos_y = posy; |
| 19421 | + info.out_width = outw; |
| 19422 | + info.out_height = outh; |
| 19423 | + |
| 19424 | + r = ovl->set_overlay_info(ovl, &info); |
| 19425 | + if (r) { |
| 19426 | + DBG("ovl->setup_overlay_info failed\n"); |
| 19427 | + goto err; |
| 19428 | + } |
| 19429 | + |
| 19430 | + return 0; |
| 19431 | + |
| 19432 | +err: |
| 19433 | + DBG("setup_overlay failed\n"); |
| 19434 | + return r; |
| 19435 | +} |
| 19436 | + |
| 19437 | +/* apply var to the overlay */ |
| 19438 | +int omapfb_apply_changes(struct fb_info *fbi, int init) |
| 19439 | +{ |
| 19440 | + int r = 0; |
| 19441 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 19442 | + struct fb_var_screeninfo *var = &fbi->var; |
| 19443 | + struct omap_overlay *ovl; |
| 19444 | + u16 posx, posy; |
| 19445 | + u16 outw, outh; |
| 19446 | + int i; |
| 19447 | + |
| 19448 | +#ifdef DEBUG |
| 19449 | + if (omapfb_test_pattern) |
| 19450 | + fill_fb(fbi); |
| 19451 | +#endif |
| 19452 | + |
| 19453 | + for (i = 0; i < ofbi->num_overlays; i++) { |
| 19454 | + ovl = ofbi->overlays[i]; |
| 19455 | + |
| 19456 | + DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id); |
| 19457 | + |
| 19458 | + if (ofbi->region.size == 0) { |
| 19459 | + /* the fb is not available. disable the overlay */ |
| 19460 | + omapfb_overlay_enable(ovl, 0); |
| 19461 | + if (!init && ovl->manager) |
| 19462 | + ovl->manager->apply(ovl->manager); |
| 19463 | + continue; |
| 19464 | + } |
| 19465 | + |
| 19466 | + if (init || (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { |
| 19467 | + int rotation = (var->rotate + ofbi->rotation[i]) % 4; |
| 19468 | + if (rotation == FB_ROTATE_CW || |
| 19469 | + rotation == FB_ROTATE_CCW) { |
| 19470 | + outw = var->yres; |
| 19471 | + outh = var->xres; |
| 19472 | + } else { |
| 19473 | + outw = var->xres; |
| 19474 | + outh = var->yres; |
| 19475 | + } |
| 19476 | + } else { |
| 19477 | + outw = ovl->info.out_width; |
| 19478 | + outh = ovl->info.out_height; |
| 19479 | + } |
| 19480 | + |
| 19481 | + if (init) { |
| 19482 | + posx = 0; |
| 19483 | + posy = 0; |
| 19484 | + } else { |
| 19485 | + posx = ovl->info.pos_x; |
| 19486 | + posy = ovl->info.pos_y; |
| 19487 | + } |
| 19488 | + |
| 19489 | + r = omapfb_setup_overlay(fbi, ovl, posx, posy, outw, outh); |
| 19490 | + if (r) |
| 19491 | + goto err; |
| 19492 | + |
| 19493 | + if (!init && ovl->manager) |
| 19494 | + ovl->manager->apply(ovl->manager); |
| 19495 | + } |
| 19496 | + return 0; |
| 19497 | +err: |
| 19498 | + DBG("apply_changes failed\n"); |
| 19499 | + return r; |
| 19500 | +} |
| 19501 | + |
| 19502 | +/* checks var and eventually tweaks it to something supported, |
| 19503 | + * DO NOT MODIFY PAR */ |
| 19504 | +static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) |
| 19505 | +{ |
| 19506 | + int r; |
| 19507 | + |
| 19508 | + DBG("check_var(%d)\n", FB2OFB(fbi)->id); |
| 19509 | + |
| 19510 | + r = check_fb_var(fbi, var); |
| 19511 | + |
| 19512 | + return r; |
| 19513 | +} |
| 19514 | + |
| 19515 | +/* set the video mode according to info->var */ |
| 19516 | +static int omapfb_set_par(struct fb_info *fbi) |
| 19517 | +{ |
| 19518 | + int r; |
| 19519 | + |
| 19520 | + DBG("set_par(%d)\n", FB2OFB(fbi)->id); |
| 19521 | + |
| 19522 | + set_fb_fix(fbi); |
| 19523 | + r = omapfb_apply_changes(fbi, 0); |
| 19524 | + |
| 19525 | + return r; |
| 19526 | +} |
| 19527 | + |
| 19528 | +static int omapfb_pan_display(struct fb_var_screeninfo *var, |
| 19529 | + struct fb_info *fbi) |
| 19530 | +{ |
| 19531 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 19532 | + int r = 0; |
| 19533 | + |
| 19534 | + DBG("pan_display(%d)\n", ofbi->id); |
| 19535 | + |
| 19536 | + if (var->xoffset != fbi->var.xoffset || |
| 19537 | + var->yoffset != fbi->var.yoffset) { |
| 19538 | + struct fb_var_screeninfo new_var; |
| 19539 | + |
| 19540 | + new_var = fbi->var; |
| 19541 | + new_var.xoffset = var->xoffset; |
| 19542 | + new_var.yoffset = var->yoffset; |
| 19543 | + |
| 19544 | + r = check_fb_var(fbi, &new_var); |
| 19545 | + |
| 19546 | + if (r == 0) { |
| 19547 | + fbi->var = new_var; |
| 19548 | + set_fb_fix(fbi); |
| 19549 | + r = omapfb_apply_changes(fbi, 0); |
| 19550 | + } |
| 19551 | + } |
| 19552 | + |
| 19553 | + return r; |
| 19554 | +} |
| 19555 | + |
| 19556 | +static void mmap_user_open(struct vm_area_struct *vma) |
| 19557 | +{ |
| 19558 | + struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data; |
| 19559 | + |
| 19560 | + atomic_inc(&ofbi->map_count); |
| 19561 | +} |
| 19562 | + |
| 19563 | +static void mmap_user_close(struct vm_area_struct *vma) |
| 19564 | +{ |
| 19565 | + struct omapfb_info *ofbi = (struct omapfb_info *)vma->vm_private_data; |
| 19566 | + |
| 19567 | + atomic_dec(&ofbi->map_count); |
| 19568 | +} |
| 19569 | + |
| 19570 | +static struct vm_operations_struct mmap_user_ops = { |
| 19571 | + .open = mmap_user_open, |
| 19572 | + .close = mmap_user_close, |
| 19573 | +}; |
| 19574 | + |
| 19575 | +static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) |
| 19576 | +{ |
| 19577 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 19578 | + struct fb_fix_screeninfo *fix = &fbi->fix; |
| 19579 | + unsigned long off; |
| 19580 | + unsigned long start; |
| 19581 | + u32 len; |
| 19582 | + |
| 19583 | + if (vma->vm_end - vma->vm_start == 0) |
| 19584 | + return 0; |
| 19585 | + if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) |
| 19586 | + return -EINVAL; |
| 19587 | + off = vma->vm_pgoff << PAGE_SHIFT; |
| 19588 | + |
| 19589 | + start = omapfb_get_region_paddr(ofbi); |
| 19590 | + len = fix->smem_len; |
| 19591 | + if (off >= len) |
| 19592 | + return -EINVAL; |
| 19593 | + if ((vma->vm_end - vma->vm_start + off) > len) |
| 19594 | + return -EINVAL; |
| 19595 | + |
| 19596 | + off += start; |
| 19597 | + |
| 19598 | + DBG("user mmap region start %lx, len %d, off %lx\n", start, len, off); |
| 19599 | + |
| 19600 | + vma->vm_pgoff = off >> PAGE_SHIFT; |
| 19601 | + vma->vm_flags |= VM_IO | VM_RESERVED; |
| 19602 | + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); |
| 19603 | + vma->vm_ops = &mmap_user_ops; |
| 19604 | + vma->vm_private_data = ofbi; |
| 19605 | + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, |
| 19606 | + vma->vm_end - vma->vm_start, vma->vm_page_prot)) |
| 19607 | + return -EAGAIN; |
| 19608 | + /* vm_ops.open won't be called for mmap itself. */ |
| 19609 | + atomic_inc(&ofbi->map_count); |
| 19610 | + return 0; |
| 19611 | +} |
| 19612 | + |
| 19613 | +/* Store a single color palette entry into a pseudo palette or the hardware |
| 19614 | + * palette if one is available. For now we support only 16bpp and thus store |
| 19615 | + * the entry only to the pseudo palette. |
| 19616 | + */ |
| 19617 | +static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green, |
| 19618 | + u_int blue, u_int transp, int update_hw_pal) |
| 19619 | +{ |
| 19620 | + /*struct omapfb_info *ofbi = FB2OFB(fbi);*/ |
| 19621 | + /*struct omapfb2_device *fbdev = ofbi->fbdev;*/ |
| 19622 | + struct fb_var_screeninfo *var = &fbi->var; |
| 19623 | + int r = 0; |
| 19624 | + |
| 19625 | + enum omapfb_color_format mode = OMAPFB_COLOR_RGB24U; /* XXX */ |
| 19626 | + |
| 19627 | + /*switch (plane->color_mode) {*/ |
| 19628 | + switch (mode) { |
| 19629 | + case OMAPFB_COLOR_YUV422: |
| 19630 | + case OMAPFB_COLOR_YUV420: |
| 19631 | + case OMAPFB_COLOR_YUY422: |
| 19632 | + r = -EINVAL; |
| 19633 | + break; |
| 19634 | + case OMAPFB_COLOR_CLUT_8BPP: |
| 19635 | + case OMAPFB_COLOR_CLUT_4BPP: |
| 19636 | + case OMAPFB_COLOR_CLUT_2BPP: |
| 19637 | + case OMAPFB_COLOR_CLUT_1BPP: |
| 19638 | + /* |
| 19639 | + if (fbdev->ctrl->setcolreg) |
| 19640 | + r = fbdev->ctrl->setcolreg(regno, red, green, blue, |
| 19641 | + transp, update_hw_pal); |
| 19642 | + */ |
| 19643 | + /* Fallthrough */ |
| 19644 | + r = -EINVAL; |
| 19645 | + break; |
| 19646 | + case OMAPFB_COLOR_RGB565: |
| 19647 | + case OMAPFB_COLOR_RGB444: |
| 19648 | + case OMAPFB_COLOR_RGB24P: |
| 19649 | + case OMAPFB_COLOR_RGB24U: |
| 19650 | + if (r != 0) |
| 19651 | + break; |
| 19652 | + |
| 19653 | + if (regno < 0) { |
| 19654 | + r = -EINVAL; |
| 19655 | + break; |
| 19656 | + } |
| 19657 | + |
| 19658 | + if (regno < 16) { |
| 19659 | + u16 pal; |
| 19660 | + pal = ((red >> (16 - var->red.length)) << |
| 19661 | + var->red.offset) | |
| 19662 | + ((green >> (16 - var->green.length)) << |
| 19663 | + var->green.offset) | |
| 19664 | + (blue >> (16 - var->blue.length)); |
| 19665 | + ((u32 *)(fbi->pseudo_palette))[regno] = pal; |
| 19666 | + } |
| 19667 | + break; |
| 19668 | + default: |
| 19669 | + BUG(); |
| 19670 | + } |
| 19671 | + return r; |
| 19672 | +} |
| 19673 | + |
| 19674 | +static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, |
| 19675 | + u_int transp, struct fb_info *info) |
| 19676 | +{ |
| 19677 | + DBG("setcolreg\n"); |
| 19678 | + |
| 19679 | + return _setcolreg(info, regno, red, green, blue, transp, 1); |
| 19680 | +} |
| 19681 | + |
| 19682 | +static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info) |
| 19683 | +{ |
| 19684 | + int count, index, r; |
| 19685 | + u16 *red, *green, *blue, *transp; |
| 19686 | + u16 trans = 0xffff; |
| 19687 | + |
| 19688 | + DBG("setcmap\n"); |
| 19689 | + |
| 19690 | + red = cmap->red; |
| 19691 | + green = cmap->green; |
| 19692 | + blue = cmap->blue; |
| 19693 | + transp = cmap->transp; |
| 19694 | + index = cmap->start; |
| 19695 | + |
| 19696 | + for (count = 0; count < cmap->len; count++) { |
| 19697 | + if (transp) |
| 19698 | + trans = *transp++; |
| 19699 | + r = _setcolreg(info, index++, *red++, *green++, *blue++, trans, |
| 19700 | + count == cmap->len - 1); |
| 19701 | + if (r != 0) |
| 19702 | + return r; |
| 19703 | + } |
| 19704 | + |
| 19705 | + return 0; |
| 19706 | +} |
| 19707 | + |
| 19708 | +static void omapfb_vrfb_suspend_all(struct omapfb2_device *fbdev) |
| 19709 | +{ |
| 19710 | + int i; |
| 19711 | + |
| 19712 | + for (i = 0; i < fbdev->num_fbs; i++) { |
| 19713 | + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); |
| 19714 | + |
| 19715 | + if (ofbi->region.vrfb.vaddr[0]) |
| 19716 | + omap_vrfb_suspend_ctx(&ofbi->region.vrfb); |
| 19717 | + } |
| 19718 | +} |
| 19719 | + |
| 19720 | +static void omapfb_vrfb_resume_all(struct omapfb2_device *fbdev) |
| 19721 | +{ |
| 19722 | + int i; |
| 19723 | + |
| 19724 | + for (i = 0; i < fbdev->num_fbs; i++) { |
| 19725 | + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); |
| 19726 | + |
| 19727 | + if (ofbi->region.vrfb.vaddr[0]) |
| 19728 | + omap_vrfb_resume_ctx(&ofbi->region.vrfb); |
| 19729 | + } |
| 19730 | +} |
| 19731 | + |
| 19732 | +static int omapfb_blank(int blank, struct fb_info *fbi) |
| 19733 | +{ |
| 19734 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 19735 | + struct omapfb2_device *fbdev = ofbi->fbdev; |
| 19736 | + struct omap_dss_device *display = fb2display(fbi); |
| 19737 | + int do_update = 0; |
| 19738 | + int r = 0; |
| 19739 | + |
| 19740 | + omapfb_lock(fbdev); |
| 19741 | + |
| 19742 | + switch (blank) { |
| 19743 | + case FB_BLANK_UNBLANK: |
| 19744 | + if (display->state != OMAP_DSS_DISPLAY_SUSPENDED) |
| 19745 | + goto exit; |
| 19746 | + |
| 19747 | + omapfb_vrfb_resume_all(fbdev); |
| 19748 | + |
| 19749 | + if (display->resume) |
| 19750 | + r = display->resume(display); |
| 19751 | + |
| 19752 | + if (r == 0 && display->get_update_mode && |
| 19753 | + display->get_update_mode(display) == |
| 19754 | + OMAP_DSS_UPDATE_MANUAL) |
| 19755 | + do_update = 1; |
| 19756 | + |
| 19757 | + break; |
| 19758 | + |
| 19759 | + case FB_BLANK_NORMAL: |
| 19760 | + /* FB_BLANK_NORMAL could be implemented. |
| 19761 | + * Needs DSS additions. */ |
| 19762 | + case FB_BLANK_VSYNC_SUSPEND: |
| 19763 | + case FB_BLANK_HSYNC_SUSPEND: |
| 19764 | + case FB_BLANK_POWERDOWN: |
| 19765 | + if (display->state != OMAP_DSS_DISPLAY_ACTIVE) |
| 19766 | + goto exit; |
| 19767 | + |
| 19768 | + if (display->suspend) |
| 19769 | + r = display->suspend(display); |
| 19770 | + |
| 19771 | + omapfb_vrfb_suspend_all(fbdev); |
| 19772 | + |
| 19773 | + break; |
| 19774 | + |
| 19775 | + default: |
| 19776 | + r = -EINVAL; |
| 19777 | + } |
| 19778 | + |
| 19779 | +exit: |
| 19780 | + omapfb_unlock(fbdev); |
| 19781 | + |
| 19782 | + if (r == 0 && do_update && display->update) { |
| 19783 | + u16 w, h; |
| 19784 | + display->get_resolution(display, &w, &h); |
| 19785 | + |
| 19786 | + r = display->update(display, 0, 0, w, h); |
| 19787 | + } |
| 19788 | + |
| 19789 | + return r; |
| 19790 | +} |
| 19791 | + |
| 19792 | +#if 0 |
| 19793 | +/* XXX fb_read and fb_write are needed for VRFB */ |
| 19794 | +ssize_t omapfb_write(struct fb_info *info, const char __user *buf, |
| 19795 | + size_t count, loff_t *ppos) |
| 19796 | +{ |
| 19797 | + DBG("omapfb_write %d, %lu\n", count, (unsigned long)*ppos); |
| 19798 | + /* XXX needed for VRFB */ |
| 19799 | + return count; |
| 19800 | +} |
| 19801 | +#endif |
| 19802 | + |
| 19803 | +static struct fb_ops omapfb_ops = { |
| 19804 | + .owner = THIS_MODULE, |
| 19805 | + .fb_open = omapfb_open, |
| 19806 | + .fb_release = omapfb_release, |
| 19807 | + .fb_fillrect = cfb_fillrect, |
| 19808 | + .fb_copyarea = cfb_copyarea, |
| 19809 | + .fb_imageblit = cfb_imageblit, |
| 19810 | + .fb_blank = omapfb_blank, |
| 19811 | + .fb_ioctl = omapfb_ioctl, |
| 19812 | + .fb_check_var = omapfb_check_var, |
| 19813 | + .fb_set_par = omapfb_set_par, |
| 19814 | + .fb_pan_display = omapfb_pan_display, |
| 19815 | + .fb_mmap = omapfb_mmap, |
| 19816 | + .fb_setcolreg = omapfb_setcolreg, |
| 19817 | + .fb_setcmap = omapfb_setcmap, |
| 19818 | + /*.fb_write = omapfb_write,*/ |
| 19819 | +}; |
| 19820 | + |
| 19821 | +static void omapfb_free_fbmem(struct fb_info *fbi) |
| 19822 | +{ |
| 19823 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 19824 | + struct omapfb2_device *fbdev = ofbi->fbdev; |
| 19825 | + struct omapfb2_mem_region *rg; |
| 19826 | + |
| 19827 | + rg = &ofbi->region; |
| 19828 | + |
| 19829 | + if (rg->paddr) |
| 19830 | + if (omap_vram_free(rg->paddr, rg->size)) |
| 19831 | + dev_err(fbdev->dev, "VRAM FREE failed\n"); |
| 19832 | + |
| 19833 | + if (rg->vaddr) |
| 19834 | + iounmap(rg->vaddr); |
| 19835 | + |
| 19836 | + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { |
| 19837 | + /* unmap the 0 angle rotation */ |
| 19838 | + if (rg->vrfb.vaddr[0]) { |
| 19839 | + iounmap(rg->vrfb.vaddr[0]); |
| 19840 | + omap_vrfb_release_ctx(&rg->vrfb); |
| 19841 | + } |
| 19842 | + } |
| 19843 | + |
| 19844 | + rg->vaddr = NULL; |
| 19845 | + rg->paddr = 0; |
| 19846 | + rg->alloc = 0; |
| 19847 | + rg->size = 0; |
| 19848 | +} |
| 19849 | + |
| 19850 | +static void clear_fb_info(struct fb_info *fbi) |
| 19851 | +{ |
| 19852 | + memset(&fbi->var, 0, sizeof(fbi->var)); |
| 19853 | + memset(&fbi->fix, 0, sizeof(fbi->fix)); |
| 19854 | + strlcpy(fbi->fix.id, MODULE_NAME, sizeof(fbi->fix.id)); |
| 19855 | +} |
| 19856 | + |
| 19857 | +static int omapfb_free_all_fbmem(struct omapfb2_device *fbdev) |
| 19858 | +{ |
| 19859 | + int i; |
| 19860 | + |
| 19861 | + DBG("free all fbmem\n"); |
| 19862 | + |
| 19863 | + for (i = 0; i < fbdev->num_fbs; i++) { |
| 19864 | + struct fb_info *fbi = fbdev->fbs[i]; |
| 19865 | + omapfb_free_fbmem(fbi); |
| 19866 | + clear_fb_info(fbi); |
| 19867 | + } |
| 19868 | + |
| 19869 | + return 0; |
| 19870 | +} |
| 19871 | + |
| 19872 | +static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, |
| 19873 | + unsigned long paddr) |
| 19874 | +{ |
| 19875 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 19876 | + struct omapfb2_device *fbdev = ofbi->fbdev; |
| 19877 | + struct omapfb2_mem_region *rg; |
| 19878 | + void __iomem *vaddr; |
| 19879 | + int r; |
| 19880 | + |
| 19881 | + rg = &ofbi->region; |
| 19882 | + memset(rg, 0, sizeof(*rg)); |
| 19883 | + |
| 19884 | + size = PAGE_ALIGN(size); |
| 19885 | + |
| 19886 | + if (!paddr) { |
| 19887 | + DBG("allocating %lu bytes for fb %d\n", size, ofbi->id); |
| 19888 | + r = omap_vram_alloc(OMAP_VRAM_MEMTYPE_SDRAM, size, &paddr); |
| 19889 | + } else { |
| 19890 | + DBG("reserving %lu bytes at %lx for fb %d\n", size, paddr, |
| 19891 | + ofbi->id); |
| 19892 | + r = omap_vram_reserve(paddr, size); |
| 19893 | + } |
| 19894 | + |
| 19895 | + if (r) { |
| 19896 | + dev_err(fbdev->dev, "failed to allocate framebuffer\n"); |
| 19897 | + return -ENOMEM; |
| 19898 | + } |
| 19899 | + |
| 19900 | + if (ofbi->rotation_type != OMAP_DSS_ROT_VRFB) { |
| 19901 | + vaddr = ioremap_wc(paddr, size); |
| 19902 | + |
| 19903 | + if (!vaddr) { |
| 19904 | + dev_err(fbdev->dev, "failed to ioremap framebuffer\n"); |
| 19905 | + omap_vram_free(paddr, size); |
| 19906 | + return -ENOMEM; |
| 19907 | + } |
| 19908 | + |
| 19909 | + DBG("allocated VRAM paddr %lx, vaddr %p\n", paddr, vaddr); |
| 19910 | + } else { |
| 19911 | + void __iomem *va; |
| 19912 | + |
| 19913 | + r = omap_vrfb_request_ctx(&rg->vrfb); |
| 19914 | + if (r) { |
| 19915 | + dev_err(fbdev->dev, "vrfb create ctx failed\n"); |
| 19916 | + return r; |
| 19917 | + } |
| 19918 | + |
| 19919 | + /* only ioremap the 0 angle view */ |
| 19920 | + va = ioremap_wc(rg->vrfb.paddr[0], size); |
| 19921 | + |
| 19922 | + if (!va) { |
| 19923 | + printk(KERN_ERR "vrfb: ioremap failed\n"); |
| 19924 | + omap_vrfb_release_ctx(&rg->vrfb); |
| 19925 | + return -ENOMEM; |
| 19926 | + } |
| 19927 | + |
| 19928 | + DBG("ioremapped vrfb area 0 to %p\n", va); |
| 19929 | + |
| 19930 | + rg->vrfb.vaddr[0] = va; |
| 19931 | + |
| 19932 | + vaddr = NULL; |
| 19933 | + } |
| 19934 | + |
| 19935 | + rg->paddr = paddr; |
| 19936 | + rg->vaddr = vaddr; |
| 19937 | + rg->size = size; |
| 19938 | + rg->alloc = 1; |
| 19939 | + |
| 19940 | + return 0; |
| 19941 | +} |
| 19942 | + |
| 19943 | +/* allocate fbmem using display resolution as reference */ |
| 19944 | +static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size, |
| 19945 | + unsigned long paddr) |
| 19946 | +{ |
| 19947 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 19948 | + struct omap_dss_device *display; |
| 19949 | + int bytespp; |
| 19950 | + |
| 19951 | + display = fb2display(fbi); |
| 19952 | + |
| 19953 | + if (!display) |
| 19954 | + return 0; |
| 19955 | + |
| 19956 | + switch (display->get_recommended_bpp(display)) { |
| 19957 | + case 16: |
| 19958 | + bytespp = 2; |
| 19959 | + break; |
| 19960 | + case 24: |
| 19961 | + bytespp = 4; |
| 19962 | + break; |
| 19963 | + default: |
| 19964 | + bytespp = 4; |
| 19965 | + break; |
| 19966 | + } |
| 19967 | + |
| 19968 | + if (!size) { |
| 19969 | + u16 w, h; |
| 19970 | + |
| 19971 | + display->get_resolution(display, &w, &h); |
| 19972 | + |
| 19973 | + if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { |
| 19974 | +#ifdef DEBUG |
| 19975 | + int oldw = w, oldh = h; |
| 19976 | +#endif |
| 19977 | + |
| 19978 | + omap_vrfb_adjust_size(&w, &h, bytespp); |
| 19979 | + |
| 19980 | + /* Because we change the resolution of the 0 degree |
| 19981 | + * view, we need to alloc max(w, h) for height */ |
| 19982 | + h = max(w, h); |
| 19983 | + w = OMAP_VRFB_LINE_LEN; |
| 19984 | + |
| 19985 | + DBG("adjusting fb mem size for VRFB, %dx%d -> %dx%d\n", |
| 19986 | + oldw, oldh, w, h); |
| 19987 | + } |
| 19988 | + |
| 19989 | + size = w * h * bytespp; |
| 19990 | + } |
| 19991 | + |
| 19992 | + if (!size) |
| 19993 | + return 0; |
| 19994 | + |
| 19995 | + return omapfb_alloc_fbmem(fbi, size, paddr); |
| 19996 | +} |
| 19997 | + |
| 19998 | +static enum omap_color_mode fb_format_to_dss_mode(enum omapfb_color_format fmt) |
| 19999 | +{ |
| 20000 | + enum omap_color_mode mode; |
| 20001 | + |
| 20002 | + switch (fmt) { |
| 20003 | + case OMAPFB_COLOR_RGB565: |
| 20004 | + mode = OMAP_DSS_COLOR_RGB16; |
| 20005 | + break; |
| 20006 | + case OMAPFB_COLOR_YUV422: |
| 20007 | + mode = OMAP_DSS_COLOR_YUV2; |
| 20008 | + break; |
| 20009 | + case OMAPFB_COLOR_CLUT_8BPP: |
| 20010 | + mode = OMAP_DSS_COLOR_CLUT8; |
| 20011 | + break; |
| 20012 | + case OMAPFB_COLOR_CLUT_4BPP: |
| 20013 | + mode = OMAP_DSS_COLOR_CLUT4; |
| 20014 | + break; |
| 20015 | + case OMAPFB_COLOR_CLUT_2BPP: |
| 20016 | + mode = OMAP_DSS_COLOR_CLUT2; |
| 20017 | + break; |
| 20018 | + case OMAPFB_COLOR_CLUT_1BPP: |
| 20019 | + mode = OMAP_DSS_COLOR_CLUT1; |
| 20020 | + break; |
| 20021 | + case OMAPFB_COLOR_RGB444: |
| 20022 | + mode = OMAP_DSS_COLOR_RGB12U; |
| 20023 | + break; |
| 20024 | + case OMAPFB_COLOR_YUY422: |
| 20025 | + mode = OMAP_DSS_COLOR_UYVY; |
| 20026 | + break; |
| 20027 | + case OMAPFB_COLOR_ARGB16: |
| 20028 | + mode = OMAP_DSS_COLOR_ARGB16; |
| 20029 | + break; |
| 20030 | + case OMAPFB_COLOR_RGB24U: |
| 20031 | + mode = OMAP_DSS_COLOR_RGB24U; |
| 20032 | + break; |
| 20033 | + case OMAPFB_COLOR_RGB24P: |
| 20034 | + mode = OMAP_DSS_COLOR_RGB24P; |
| 20035 | + break; |
| 20036 | + case OMAPFB_COLOR_ARGB32: |
| 20037 | + mode = OMAP_DSS_COLOR_ARGB32; |
| 20038 | + break; |
| 20039 | + case OMAPFB_COLOR_RGBA32: |
| 20040 | + mode = OMAP_DSS_COLOR_RGBA32; |
| 20041 | + break; |
| 20042 | + case OMAPFB_COLOR_RGBX32: |
| 20043 | + mode = OMAP_DSS_COLOR_RGBX32; |
| 20044 | + break; |
| 20045 | + default: |
| 20046 | + mode = -EINVAL; |
| 20047 | + } |
| 20048 | + |
| 20049 | + return mode; |
| 20050 | +} |
| 20051 | + |
| 20052 | +static int omapfb_parse_vram_param(const char *param, int max_entries, |
| 20053 | + unsigned long *sizes, unsigned long *paddrs) |
| 20054 | +{ |
| 20055 | + int fbnum; |
| 20056 | + unsigned long size; |
| 20057 | + unsigned long paddr = 0; |
| 20058 | + char *p, *start; |
| 20059 | + |
| 20060 | + start = (char *)param; |
| 20061 | + |
| 20062 | + while (1) { |
| 20063 | + p = start; |
| 20064 | + |
| 20065 | + fbnum = simple_strtoul(p, &p, 10); |
| 20066 | + |
| 20067 | + if (p == param) |
| 20068 | + return -EINVAL; |
| 20069 | + |
| 20070 | + if (*p != ':') |
| 20071 | + return -EINVAL; |
| 20072 | + |
| 20073 | + if (fbnum >= max_entries) |
| 20074 | + return -EINVAL; |
| 20075 | + |
| 20076 | + size = memparse(p + 1, &p); |
| 20077 | + |
| 20078 | + if (!size) |
| 20079 | + return -EINVAL; |
| 20080 | + |
| 20081 | + paddr = 0; |
| 20082 | + |
| 20083 | + if (*p == '@') { |
| 20084 | + paddr = simple_strtoul(p + 1, &p, 16); |
| 20085 | + |
| 20086 | + if (!paddr) |
| 20087 | + return -EINVAL; |
| 20088 | + |
| 20089 | + } |
| 20090 | + |
| 20091 | + paddrs[fbnum] = paddr; |
| 20092 | + sizes[fbnum] = size; |
| 20093 | + |
| 20094 | + if (*p == 0) |
| 20095 | + break; |
| 20096 | + |
| 20097 | + if (*p != ',') |
| 20098 | + return -EINVAL; |
| 20099 | + |
| 20100 | + ++p; |
| 20101 | + |
| 20102 | + start = p; |
| 20103 | + } |
| 20104 | + |
| 20105 | + return 0; |
| 20106 | +} |
| 20107 | + |
| 20108 | +static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev) |
| 20109 | +{ |
| 20110 | + int i, r; |
| 20111 | + unsigned long vram_sizes[10]; |
| 20112 | + unsigned long vram_paddrs[10]; |
| 20113 | + |
| 20114 | + memset(&vram_sizes, 0, sizeof(vram_sizes)); |
| 20115 | + memset(&vram_paddrs, 0, sizeof(vram_paddrs)); |
| 20116 | + |
| 20117 | + if (def_vram && omapfb_parse_vram_param(def_vram, 10, |
| 20118 | + vram_sizes, vram_paddrs)) { |
| 20119 | + dev_err(fbdev->dev, "failed to parse vram parameter\n"); |
| 20120 | + |
| 20121 | + memset(&vram_sizes, 0, sizeof(vram_sizes)); |
| 20122 | + memset(&vram_paddrs, 0, sizeof(vram_paddrs)); |
| 20123 | + } |
| 20124 | + |
| 20125 | + if (fbdev->dev->platform_data) { |
| 20126 | + struct omapfb_platform_data *opd; |
| 20127 | + opd = fbdev->dev->platform_data; |
| 20128 | + for (i = 0; i < opd->mem_desc.region_cnt; ++i) { |
| 20129 | + if (!vram_sizes[i]) { |
| 20130 | + unsigned long size; |
| 20131 | + unsigned long paddr; |
| 20132 | + |
| 20133 | + size = opd->mem_desc.region[i].size; |
| 20134 | + paddr = opd->mem_desc.region[i].paddr; |
| 20135 | + |
| 20136 | + vram_sizes[i] = size; |
| 20137 | + vram_paddrs[i] = paddr; |
| 20138 | + } |
| 20139 | + } |
| 20140 | + } |
| 20141 | + |
| 20142 | + for (i = 0; i < fbdev->num_fbs; i++) { |
| 20143 | + /* allocate memory automatically only for fb0, or if |
| 20144 | + * excplicitly defined with vram or plat data option */ |
| 20145 | + if (i == 0 || vram_sizes[i] != 0) { |
| 20146 | + r = omapfb_alloc_fbmem_display(fbdev->fbs[i], |
| 20147 | + vram_sizes[i], vram_paddrs[i]); |
| 20148 | + |
| 20149 | + if (r) |
| 20150 | + return r; |
| 20151 | + } |
| 20152 | + } |
| 20153 | + |
| 20154 | + for (i = 0; i < fbdev->num_fbs; i++) { |
| 20155 | + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); |
| 20156 | + struct omapfb2_mem_region *rg; |
| 20157 | + rg = &ofbi->region; |
| 20158 | + |
| 20159 | + DBG("region%d phys %08x virt %p size=%lu\n", |
| 20160 | + i, |
| 20161 | + rg->paddr, |
| 20162 | + rg->vaddr, |
| 20163 | + rg->size); |
| 20164 | + } |
| 20165 | + |
| 20166 | + return 0; |
| 20167 | +} |
| 20168 | + |
| 20169 | +int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type) |
| 20170 | +{ |
| 20171 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 20172 | + struct omapfb2_device *fbdev = ofbi->fbdev; |
| 20173 | + struct omap_dss_device *display = fb2display(fbi); |
| 20174 | + struct omapfb2_mem_region *rg = &ofbi->region; |
| 20175 | + unsigned long old_size = rg->size; |
| 20176 | + unsigned long old_paddr = rg->paddr; |
| 20177 | + int old_type = rg->type; |
| 20178 | + int r; |
| 20179 | + |
| 20180 | + if (type > OMAPFB_MEMTYPE_MAX) |
| 20181 | + return -EINVAL; |
| 20182 | + |
| 20183 | + size = PAGE_ALIGN(size); |
| 20184 | + |
| 20185 | + if (old_size == size && old_type == type) |
| 20186 | + return 0; |
| 20187 | + |
| 20188 | + if (display && display->sync) |
| 20189 | + display->sync(display); |
| 20190 | + |
| 20191 | + omapfb_free_fbmem(fbi); |
| 20192 | + |
| 20193 | + if (size == 0) { |
| 20194 | + clear_fb_info(fbi); |
| 20195 | + return 0; |
| 20196 | + } |
| 20197 | + |
| 20198 | + r = omapfb_alloc_fbmem(fbi, size, 0); |
| 20199 | + |
| 20200 | + if (r) { |
| 20201 | + if (old_size) |
| 20202 | + omapfb_alloc_fbmem(fbi, old_size, old_paddr); |
| 20203 | + |
| 20204 | + if (rg->size == 0) |
| 20205 | + clear_fb_info(fbi); |
| 20206 | + |
| 20207 | + return r; |
| 20208 | + } |
| 20209 | + |
| 20210 | + if (old_size == size) |
| 20211 | + return 0; |
| 20212 | + |
| 20213 | + if (old_size == 0) { |
| 20214 | + DBG("initializing fb %d\n", ofbi->id); |
| 20215 | + r = omapfb_fb_init(fbdev, fbi); |
| 20216 | + if (r) { |
| 20217 | + DBG("omapfb_fb_init failed\n"); |
| 20218 | + goto err; |
| 20219 | + } |
| 20220 | + r = omapfb_apply_changes(fbi, 1); |
| 20221 | + if (r) { |
| 20222 | + DBG("omapfb_apply_changes failed\n"); |
| 20223 | + goto err; |
| 20224 | + } |
| 20225 | + } else { |
| 20226 | + struct fb_var_screeninfo new_var; |
| 20227 | + memcpy(&new_var, &fbi->var, sizeof(new_var)); |
| 20228 | + r = check_fb_var(fbi, &new_var); |
| 20229 | + if (r) |
| 20230 | + goto err; |
| 20231 | + memcpy(&fbi->var, &new_var, sizeof(fbi->var)); |
| 20232 | + set_fb_fix(fbi); |
| 20233 | + } |
| 20234 | + |
| 20235 | + return 0; |
| 20236 | +err: |
| 20237 | + omapfb_free_fbmem(fbi); |
| 20238 | + clear_fb_info(fbi); |
| 20239 | + return r; |
| 20240 | +} |
| 20241 | + |
| 20242 | +/* initialize fb_info, var, fix to something sane based on the display */ |
| 20243 | +static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi) |
| 20244 | +{ |
| 20245 | + struct fb_var_screeninfo *var = &fbi->var; |
| 20246 | + struct omap_dss_device *display = fb2display(fbi); |
| 20247 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 20248 | + int r = 0; |
| 20249 | + |
| 20250 | + fbi->fbops = &omapfb_ops; |
| 20251 | + fbi->flags = FBINFO_FLAG_DEFAULT; |
| 20252 | + fbi->pseudo_palette = fbdev->pseudo_palette; |
| 20253 | + |
| 20254 | + if (ofbi->region.size == 0) { |
| 20255 | + clear_fb_info(fbi); |
| 20256 | + return 0; |
| 20257 | + } |
| 20258 | + |
| 20259 | + var->nonstd = 0; |
| 20260 | + var->bits_per_pixel = 0; |
| 20261 | + |
| 20262 | + var->rotate = def_rotate; |
| 20263 | + |
| 20264 | + /* |
| 20265 | + * Check if there is a default color format set in the board file, |
| 20266 | + * and use this format instead the default deducted from the |
| 20267 | + * display bpp. |
| 20268 | + */ |
| 20269 | + if (fbdev->dev->platform_data) { |
| 20270 | + struct omapfb_platform_data *opd; |
| 20271 | + int id = ofbi->id; |
| 20272 | + |
| 20273 | + opd = fbdev->dev->platform_data; |
| 20274 | + if (opd->mem_desc.region[id].format_used) { |
| 20275 | + enum omap_color_mode mode; |
| 20276 | + enum omapfb_color_format format; |
| 20277 | + |
| 20278 | + format = opd->mem_desc.region[id].format; |
| 20279 | + mode = fb_format_to_dss_mode(format); |
| 20280 | + if (mode < 0) { |
| 20281 | + r = mode; |
| 20282 | + goto err; |
| 20283 | + } |
| 20284 | + r = dss_mode_to_fb_mode(mode, var); |
| 20285 | + if (r < 0) |
| 20286 | + goto err; |
| 20287 | + } |
| 20288 | + } |
| 20289 | + |
| 20290 | + if (display) { |
| 20291 | + u16 w, h; |
| 20292 | + int rotation = (var->rotate + ofbi->rotation[0]) % 4; |
| 20293 | + |
| 20294 | + display->get_resolution(display, &w, &h); |
| 20295 | + |
| 20296 | + if (rotation == FB_ROTATE_CW || |
| 20297 | + rotation == FB_ROTATE_CCW) { |
| 20298 | + var->xres = h; |
| 20299 | + var->yres = w; |
| 20300 | + } else { |
| 20301 | + var->xres = w; |
| 20302 | + var->yres = h; |
| 20303 | + } |
| 20304 | + |
| 20305 | + var->xres_virtual = var->xres; |
| 20306 | + var->yres_virtual = var->yres; |
| 20307 | + |
| 20308 | + if (!var->bits_per_pixel) { |
| 20309 | + switch (display->get_recommended_bpp(display)) { |
| 20310 | + case 16: |
| 20311 | + var->bits_per_pixel = 16; |
| 20312 | + break; |
| 20313 | + case 24: |
| 20314 | + var->bits_per_pixel = 32; |
| 20315 | + break; |
| 20316 | + default: |
| 20317 | + dev_err(fbdev->dev, "illegal display " |
| 20318 | + "bpp\n"); |
| 20319 | + return -EINVAL; |
| 20320 | + } |
| 20321 | + } |
| 20322 | + } else { |
| 20323 | + /* if there's no display, let's just guess some basic values */ |
| 20324 | + var->xres = 320; |
| 20325 | + var->yres = 240; |
| 20326 | + var->xres_virtual = var->xres; |
| 20327 | + var->yres_virtual = var->yres; |
| 20328 | + if (!var->bits_per_pixel) |
| 20329 | + var->bits_per_pixel = 16; |
| 20330 | + } |
| 20331 | + |
| 20332 | + r = check_fb_var(fbi, var); |
| 20333 | + if (r) |
| 20334 | + goto err; |
| 20335 | + |
| 20336 | + set_fb_fix(fbi); |
| 20337 | + |
| 20338 | + r = fb_alloc_cmap(&fbi->cmap, 256, 0); |
| 20339 | + if (r) |
| 20340 | + dev_err(fbdev->dev, "unable to allocate color map memory\n"); |
| 20341 | + |
| 20342 | +err: |
| 20343 | + return r; |
| 20344 | +} |
| 20345 | + |
| 20346 | +static void fbinfo_cleanup(struct omapfb2_device *fbdev, struct fb_info *fbi) |
| 20347 | +{ |
| 20348 | + fb_dealloc_cmap(&fbi->cmap); |
| 20349 | +} |
| 20350 | + |
| 20351 | + |
| 20352 | +static void omapfb_free_resources(struct omapfb2_device *fbdev) |
| 20353 | +{ |
| 20354 | + int i; |
| 20355 | + |
| 20356 | + DBG("free_resources\n"); |
| 20357 | + |
| 20358 | + if (fbdev == NULL) |
| 20359 | + return; |
| 20360 | + |
| 20361 | + for (i = 0; i < fbdev->num_fbs; i++) |
| 20362 | + unregister_framebuffer(fbdev->fbs[i]); |
| 20363 | + |
| 20364 | + /* free the reserved fbmem */ |
| 20365 | + omapfb_free_all_fbmem(fbdev); |
| 20366 | + |
| 20367 | + for (i = 0; i < fbdev->num_fbs; i++) { |
| 20368 | + fbinfo_cleanup(fbdev, fbdev->fbs[i]); |
| 20369 | + framebuffer_release(fbdev->fbs[i]); |
| 20370 | + } |
| 20371 | + |
| 20372 | + for (i = 0; i < fbdev->num_displays; i++) { |
| 20373 | + if (fbdev->displays[i]->state != OMAP_DSS_DISPLAY_DISABLED) |
| 20374 | + fbdev->displays[i]->disable(fbdev->displays[i]); |
| 20375 | + |
| 20376 | + omap_dss_put_device(fbdev->displays[i]); |
| 20377 | + } |
| 20378 | + |
| 20379 | + dev_set_drvdata(fbdev->dev, NULL); |
| 20380 | + kfree(fbdev); |
| 20381 | +} |
| 20382 | + |
| 20383 | +static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) |
| 20384 | +{ |
| 20385 | + int r, i; |
| 20386 | + |
| 20387 | + fbdev->num_fbs = 0; |
| 20388 | + |
| 20389 | + DBG("create %d framebuffers\n", CONFIG_FB_OMAP2_NUM_FBS); |
| 20390 | + |
| 20391 | + /* allocate fb_infos */ |
| 20392 | + for (i = 0; i < CONFIG_FB_OMAP2_NUM_FBS; i++) { |
| 20393 | + struct fb_info *fbi; |
| 20394 | + struct omapfb_info *ofbi; |
| 20395 | + |
| 20396 | + fbi = framebuffer_alloc(sizeof(struct omapfb_info), |
| 20397 | + fbdev->dev); |
| 20398 | + |
| 20399 | + if (fbi == NULL) { |
| 20400 | + dev_err(fbdev->dev, |
| 20401 | + "unable to allocate memory for plane info\n"); |
| 20402 | + return -ENOMEM; |
| 20403 | + } |
| 20404 | + |
| 20405 | + clear_fb_info(fbi); |
| 20406 | + |
| 20407 | + fbdev->fbs[i] = fbi; |
| 20408 | + |
| 20409 | + ofbi = FB2OFB(fbi); |
| 20410 | + ofbi->fbdev = fbdev; |
| 20411 | + ofbi->id = i; |
| 20412 | + |
| 20413 | + /* assign these early, so that fb alloc can use them */ |
| 20414 | + ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB : |
| 20415 | + OMAP_DSS_ROT_DMA; |
| 20416 | + ofbi->mirror = def_mirror; |
| 20417 | + |
| 20418 | + fbdev->num_fbs++; |
| 20419 | + } |
| 20420 | + |
| 20421 | + DBG("fb_infos allocated\n"); |
| 20422 | + |
| 20423 | + /* assign overlays for the fbs */ |
| 20424 | + for (i = 0; i < min(fbdev->num_fbs, fbdev->num_overlays); i++) { |
| 20425 | + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); |
| 20426 | + |
| 20427 | + ofbi->overlays[0] = fbdev->overlays[i]; |
| 20428 | + ofbi->num_overlays = 1; |
| 20429 | + } |
| 20430 | + |
| 20431 | + /* allocate fb memories */ |
| 20432 | + r = omapfb_allocate_all_fbs(fbdev); |
| 20433 | + if (r) { |
| 20434 | + dev_err(fbdev->dev, "failed to allocate fbmem\n"); |
| 20435 | + return r; |
| 20436 | + } |
| 20437 | + |
| 20438 | + DBG("fbmems allocated\n"); |
| 20439 | + |
| 20440 | + /* setup fb_infos */ |
| 20441 | + for (i = 0; i < fbdev->num_fbs; i++) { |
| 20442 | + r = omapfb_fb_init(fbdev, fbdev->fbs[i]); |
| 20443 | + if (r) { |
| 20444 | + dev_err(fbdev->dev, "failed to setup fb_info\n"); |
| 20445 | + return r; |
| 20446 | + } |
| 20447 | + } |
| 20448 | + |
| 20449 | + DBG("fb_infos initialized\n"); |
| 20450 | + |
| 20451 | + for (i = 0; i < fbdev->num_fbs; i++) { |
| 20452 | + r = register_framebuffer(fbdev->fbs[i]); |
| 20453 | + if (r != 0) { |
| 20454 | + dev_err(fbdev->dev, |
| 20455 | + "registering framebuffer %d failed\n", i); |
| 20456 | + return r; |
| 20457 | + } |
| 20458 | + } |
| 20459 | + |
| 20460 | + DBG("framebuffers registered\n"); |
| 20461 | + |
| 20462 | + for (i = 0; i < fbdev->num_fbs; i++) { |
| 20463 | + r = omapfb_apply_changes(fbdev->fbs[i], 1); |
| 20464 | + if (r) { |
| 20465 | + dev_err(fbdev->dev, "failed to change mode\n"); |
| 20466 | + return r; |
| 20467 | + } |
| 20468 | + } |
| 20469 | + |
| 20470 | + DBG("create sysfs for fbs\n"); |
| 20471 | + r = omapfb_create_sysfs(fbdev); |
| 20472 | + if (r) { |
| 20473 | + dev_err(fbdev->dev, "failed to create sysfs entries\n"); |
| 20474 | + return r; |
| 20475 | + } |
| 20476 | + |
| 20477 | + /* Enable fb0 */ |
| 20478 | + if (fbdev->num_fbs > 0) { |
| 20479 | + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[0]); |
| 20480 | + |
| 20481 | + if (ofbi->num_overlays > 0) { |
| 20482 | + struct omap_overlay *ovl = ofbi->overlays[0]; |
| 20483 | + |
| 20484 | + r = omapfb_overlay_enable(ovl, 1); |
| 20485 | + |
| 20486 | + if (r) { |
| 20487 | + dev_err(fbdev->dev, |
| 20488 | + "failed to enable overlay\n"); |
| 20489 | + return r; |
| 20490 | + } |
| 20491 | + } |
| 20492 | + } |
| 20493 | + |
| 20494 | + DBG("create_framebuffers done\n"); |
| 20495 | + |
| 20496 | + return 0; |
| 20497 | +} |
| 20498 | + |
| 20499 | +static int omapfb_mode_to_timings(const char *mode_str, |
| 20500 | + struct omap_video_timings *timings, u8 *bpp) |
| 20501 | +{ |
| 20502 | + struct fb_info fbi; |
| 20503 | + struct fb_var_screeninfo var; |
| 20504 | + struct fb_ops fbops; |
| 20505 | + int r; |
| 20506 | + |
| 20507 | +#ifdef CONFIG_OMAP2_DSS_VENC |
| 20508 | + if (strcmp(mode_str, "pal") == 0) { |
| 20509 | + *timings = omap_dss_pal_timings; |
| 20510 | + *bpp = 0; |
| 20511 | + return 0; |
| 20512 | + } else if (strcmp(mode_str, "ntsc") == 0) { |
| 20513 | + *timings = omap_dss_ntsc_timings; |
| 20514 | + *bpp = 0; |
| 20515 | + return 0; |
| 20516 | + } |
| 20517 | +#endif |
| 20518 | + |
| 20519 | + /* this is quite a hack, but I wanted to use the modedb and for |
| 20520 | + * that we need fb_info and var, so we create dummy ones */ |
| 20521 | + |
| 20522 | + memset(&fbi, 0, sizeof(fbi)); |
| 20523 | + memset(&var, 0, sizeof(var)); |
| 20524 | + memset(&fbops, 0, sizeof(fbops)); |
| 20525 | + fbi.fbops = &fbops; |
| 20526 | + |
| 20527 | + r = fb_find_mode(&var, &fbi, mode_str, NULL, 0, NULL, 24); |
| 20528 | + |
| 20529 | + if (r != 0) { |
| 20530 | + timings->pixel_clock = PICOS2KHZ(var.pixclock); |
| 20531 | + timings->hfp = var.left_margin; |
| 20532 | + timings->hbp = var.right_margin; |
| 20533 | + timings->vfp = var.upper_margin; |
| 20534 | + timings->vbp = var.lower_margin; |
| 20535 | + timings->hsw = var.hsync_len; |
| 20536 | + timings->vsw = var.vsync_len; |
| 20537 | + timings->x_res = var.xres; |
| 20538 | + timings->y_res = var.yres; |
| 20539 | + |
| 20540 | + switch (var.bits_per_pixel) { |
| 20541 | + case 16: |
| 20542 | + *bpp = 16; |
| 20543 | + break; |
| 20544 | + case 24: |
| 20545 | + case 32: |
| 20546 | + default: |
| 20547 | + *bpp = 24; |
| 20548 | + break; |
| 20549 | + } |
| 20550 | + |
| 20551 | + return 0; |
| 20552 | + } else { |
| 20553 | + return -EINVAL; |
| 20554 | + } |
| 20555 | +} |
| 20556 | + |
| 20557 | +static int omapfb_set_def_mode(struct omap_dss_device *display, char *mode_str) |
| 20558 | +{ |
| 20559 | + int r; |
| 20560 | + u8 bpp; |
| 20561 | + struct omap_video_timings timings; |
| 20562 | + |
| 20563 | + r = omapfb_mode_to_timings(mode_str, &timings, &bpp); |
| 20564 | + if (r) |
| 20565 | + return r; |
| 20566 | + |
| 20567 | + display->panel.recommended_bpp = bpp; |
| 20568 | + |
| 20569 | + if (!display->check_timings || !display->set_timings) |
| 20570 | + return -EINVAL; |
| 20571 | + |
| 20572 | + r = display->check_timings(display, &timings); |
| 20573 | + if (r) |
| 20574 | + return r; |
| 20575 | + |
| 20576 | + display->set_timings(display, &timings); |
| 20577 | + |
| 20578 | + return 0; |
| 20579 | +} |
| 20580 | + |
| 20581 | +static int omapfb_parse_def_modes(struct omapfb2_device *fbdev) |
| 20582 | +{ |
| 20583 | + char *str, *options, *this_opt; |
| 20584 | + int r = 0; |
| 20585 | + |
| 20586 | + str = kmalloc(strlen(def_mode) + 1, GFP_KERNEL); |
| 20587 | + strcpy(str, def_mode); |
| 20588 | + options = str; |
| 20589 | + |
| 20590 | + while (!r && (this_opt = strsep(&options, ",")) != NULL) { |
| 20591 | + char *p, *display_str, *mode_str; |
| 20592 | + struct omap_dss_device *display; |
| 20593 | + int i; |
| 20594 | + |
| 20595 | + p = strchr(this_opt, ':'); |
| 20596 | + if (!p) { |
| 20597 | + r = -EINVAL; |
| 20598 | + break; |
| 20599 | + } |
| 20600 | + |
| 20601 | + *p = 0; |
| 20602 | + display_str = this_opt; |
| 20603 | + mode_str = p + 1; |
| 20604 | + |
| 20605 | + display = NULL; |
| 20606 | + for (i = 0; i < fbdev->num_displays; ++i) { |
| 20607 | + if (strcmp(fbdev->displays[i]->name, |
| 20608 | + display_str) == 0) { |
| 20609 | + display = fbdev->displays[i]; |
| 20610 | + break; |
| 20611 | + } |
| 20612 | + } |
| 20613 | + |
| 20614 | + if (!display) { |
| 20615 | + r = -EINVAL; |
| 20616 | + break; |
| 20617 | + } |
| 20618 | + |
| 20619 | + r = omapfb_set_def_mode(display, mode_str); |
| 20620 | + if (r) |
| 20621 | + break; |
| 20622 | + } |
| 20623 | + |
| 20624 | + kfree(str); |
| 20625 | + |
| 20626 | + return r; |
| 20627 | +} |
| 20628 | + |
| 20629 | +static int omapfb_probe(struct platform_device *pdev) |
| 20630 | +{ |
| 20631 | + struct omapfb2_device *fbdev = NULL; |
| 20632 | + int r = 0; |
| 20633 | + int i; |
| 20634 | + struct omap_overlay *ovl; |
| 20635 | + struct omap_dss_device *def_display; |
| 20636 | + struct omap_dss_device *dssdev; |
| 20637 | + |
| 20638 | + DBG("omapfb_probe\n"); |
| 20639 | + |
| 20640 | + if (pdev->num_resources != 0) { |
| 20641 | + dev_err(&pdev->dev, "probed for an unknown device\n"); |
| 20642 | + r = -ENODEV; |
| 20643 | + goto err0; |
| 20644 | + } |
| 20645 | + |
| 20646 | + fbdev = kzalloc(sizeof(struct omapfb2_device), GFP_KERNEL); |
| 20647 | + if (fbdev == NULL) { |
| 20648 | + r = -ENOMEM; |
| 20649 | + goto err0; |
| 20650 | + } |
| 20651 | + |
| 20652 | + mutex_init(&fbdev->mtx); |
| 20653 | + |
| 20654 | + fbdev->dev = &pdev->dev; |
| 20655 | + platform_set_drvdata(pdev, fbdev); |
| 20656 | + |
| 20657 | + fbdev->num_displays = 0; |
| 20658 | + dssdev = NULL; |
| 20659 | + for_each_dss_dev(dssdev) { |
| 20660 | + omap_dss_get_device(dssdev); |
| 20661 | + fbdev->displays[fbdev->num_displays++] = dssdev; |
| 20662 | + } |
| 20663 | + |
| 20664 | + if (fbdev->num_displays == 0) { |
| 20665 | + dev_err(&pdev->dev, "no displays\n"); |
| 20666 | + r = -EINVAL; |
| 20667 | + goto cleanup; |
| 20668 | + } |
| 20669 | + |
| 20670 | + fbdev->num_overlays = omap_dss_get_num_overlays(); |
| 20671 | + for (i = 0; i < fbdev->num_overlays; i++) |
| 20672 | + fbdev->overlays[i] = omap_dss_get_overlay(i); |
| 20673 | + |
| 20674 | + fbdev->num_managers = omap_dss_get_num_overlay_managers(); |
| 20675 | + for (i = 0; i < fbdev->num_managers; i++) |
| 20676 | + fbdev->managers[i] = omap_dss_get_overlay_manager(i); |
| 20677 | + |
| 20678 | + if (def_mode && strlen(def_mode) > 0) { |
| 20679 | + if (omapfb_parse_def_modes(fbdev)) |
| 20680 | + dev_warn(&pdev->dev, "cannot parse default modes\n"); |
| 20681 | + } |
| 20682 | + |
| 20683 | + r = omapfb_create_framebuffers(fbdev); |
| 20684 | + if (r) |
| 20685 | + goto cleanup; |
| 20686 | + |
| 20687 | + for (i = 0; i < fbdev->num_managers; i++) { |
| 20688 | + struct omap_overlay_manager *mgr; |
| 20689 | + mgr = fbdev->managers[i]; |
| 20690 | + r = mgr->apply(mgr); |
| 20691 | + if (r) |
| 20692 | + dev_warn(fbdev->dev, "failed to apply dispc config\n"); |
| 20693 | + } |
| 20694 | + |
| 20695 | + DBG("mgr->apply'ed\n"); |
| 20696 | + |
| 20697 | + /* gfx overlay should be the default one. find a display |
| 20698 | + * connected to that, and use it as default display */ |
| 20699 | + ovl = omap_dss_get_overlay(0); |
| 20700 | + if (ovl->manager && ovl->manager->device) { |
| 20701 | + def_display = ovl->manager->device; |
| 20702 | + } else { |
| 20703 | + dev_warn(&pdev->dev, "cannot find default display\n"); |
| 20704 | + def_display = NULL; |
| 20705 | + } |
| 20706 | + |
| 20707 | + if (def_display) { |
| 20708 | + u16 w, h; |
| 20709 | + r = def_display->enable(def_display); |
| 20710 | + if (r) |
| 20711 | + dev_warn(fbdev->dev, "Failed to enable display '%s'\n", |
| 20712 | + def_display->name); |
| 20713 | + |
| 20714 | + /* set the update mode */ |
| 20715 | + if (def_display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { |
| 20716 | +#ifdef CONFIG_FB_OMAP2_FORCE_AUTO_UPDATE |
| 20717 | + if (def_display->enable_te) |
| 20718 | + def_display->enable_te(def_display, 1); |
| 20719 | + if (def_display->set_update_mode) |
| 20720 | + def_display->set_update_mode(def_display, |
| 20721 | + OMAP_DSS_UPDATE_AUTO); |
| 20722 | +#else /* MANUAL_UPDATE */ |
| 20723 | + if (def_display->enable_te) |
| 20724 | + def_display->enable_te(def_display, 0); |
| 20725 | + if (def_display->set_update_mode) |
| 20726 | + def_display->set_update_mode(def_display, |
| 20727 | + OMAP_DSS_UPDATE_MANUAL); |
| 20728 | + |
| 20729 | + def_display->get_resolution(def_display, &w, &h); |
| 20730 | + def_display->update(def_display, 0, 0, w, h); |
| 20731 | +#endif |
| 20732 | + } else { |
| 20733 | + if (def_display->set_update_mode) |
| 20734 | + def_display->set_update_mode(def_display, |
| 20735 | + OMAP_DSS_UPDATE_AUTO); |
| 20736 | + } |
| 20737 | + } |
| 20738 | + |
| 20739 | + return 0; |
| 20740 | + |
| 20741 | +cleanup: |
| 20742 | + omapfb_free_resources(fbdev); |
| 20743 | +err0: |
| 20744 | + dev_err(&pdev->dev, "failed to setup omapfb\n"); |
| 20745 | + return r; |
| 20746 | +} |
| 20747 | + |
| 20748 | +static int omapfb_remove(struct platform_device *pdev) |
| 20749 | +{ |
| 20750 | + struct omapfb2_device *fbdev = platform_get_drvdata(pdev); |
| 20751 | + |
| 20752 | + /* FIXME: wait till completion of pending events */ |
| 20753 | + |
| 20754 | + omapfb_remove_sysfs(fbdev); |
| 20755 | + |
| 20756 | + omapfb_free_resources(fbdev); |
| 20757 | + |
| 20758 | + return 0; |
| 20759 | +} |
| 20760 | + |
| 20761 | +static struct platform_driver omapfb_driver = { |
| 20762 | + .probe = omapfb_probe, |
| 20763 | + .remove = omapfb_remove, |
| 20764 | + .driver = { |
| 20765 | + .name = "omapfb", |
| 20766 | + .owner = THIS_MODULE, |
| 20767 | + }, |
| 20768 | +}; |
| 20769 | + |
| 20770 | +static int __init omapfb_init(void) |
| 20771 | +{ |
| 20772 | + DBG("omapfb_init\n"); |
| 20773 | + |
| 20774 | + if (platform_driver_register(&omapfb_driver)) { |
| 20775 | + printk(KERN_ERR "failed to register omapfb driver\n"); |
| 20776 | + return -ENODEV; |
| 20777 | + } |
| 20778 | + |
| 20779 | + return 0; |
| 20780 | +} |
| 20781 | + |
| 20782 | +static void __exit omapfb_exit(void) |
| 20783 | +{ |
| 20784 | + DBG("omapfb_exit\n"); |
| 20785 | + platform_driver_unregister(&omapfb_driver); |
| 20786 | +} |
| 20787 | + |
| 20788 | +module_param_named(mode, def_mode, charp, 0); |
| 20789 | +module_param_named(vram, def_vram, charp, 0); |
| 20790 | +module_param_named(rotate, def_rotate, int, 0); |
| 20791 | +module_param_named(vrfb, def_vrfb, bool, 0); |
| 20792 | +module_param_named(mirror, def_mirror, bool, 0); |
| 20793 | + |
| 20794 | +/* late_initcall to let panel/ctrl drivers loaded first. |
| 20795 | + * I guess better option would be a more dynamic approach, |
| 20796 | + * so that omapfb reacts to new panels when they are loaded */ |
| 20797 | +late_initcall(omapfb_init); |
| 20798 | +/*module_init(omapfb_init);*/ |
| 20799 | +module_exit(omapfb_exit); |
| 20800 | + |
| 20801 | +MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); |
| 20802 | +MODULE_DESCRIPTION("OMAP2/3 Framebuffer"); |
| 20803 | +MODULE_LICENSE("GPL v2"); |
| 20804 | --- /dev/null |
| 20805 | +++ b/drivers/video/omap2/omapfb/omapfb-sysfs.c |
| 20806 | @@ -0,0 +1,507 @@ |
| 20807 | +/* |
| 20808 | + * linux/drivers/video/omap2/omapfb-sysfs.c |
| 20809 | + * |
| 20810 | + * Copyright (C) 2008 Nokia Corporation |
| 20811 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 20812 | + * |
| 20813 | + * Some code and ideas taken from drivers/video/omap/ driver |
| 20814 | + * by Imre Deak. |
| 20815 | + * |
| 20816 | + * This program is free software; you can redistribute it and/or modify it |
| 20817 | + * under the terms of the GNU General Public License version 2 as published by |
| 20818 | + * the Free Software Foundation. |
| 20819 | + * |
| 20820 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 20821 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 20822 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 20823 | + * more details. |
| 20824 | + * |
| 20825 | + * You should have received a copy of the GNU General Public License along with |
| 20826 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 20827 | + */ |
| 20828 | + |
| 20829 | +#include <linux/fb.h> |
| 20830 | +#include <linux/sysfs.h> |
| 20831 | +#include <linux/device.h> |
| 20832 | +#include <linux/uaccess.h> |
| 20833 | +#include <linux/platform_device.h> |
| 20834 | +#include <linux/kernel.h> |
| 20835 | +#include <linux/mm.h> |
| 20836 | +#include <linux/omapfb.h> |
| 20837 | + |
| 20838 | +#include <mach/display.h> |
| 20839 | +#include <mach/vrfb.h> |
| 20840 | + |
| 20841 | +#include "omapfb.h" |
| 20842 | + |
| 20843 | +static ssize_t show_rotate_type(struct device *dev, |
| 20844 | + struct device_attribute *attr, char *buf) |
| 20845 | +{ |
| 20846 | + struct fb_info *fbi = dev_get_drvdata(dev); |
| 20847 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 20848 | + |
| 20849 | + return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->rotation_type); |
| 20850 | +} |
| 20851 | + |
| 20852 | +static ssize_t store_rotate_type(struct device *dev, |
| 20853 | + struct device_attribute *attr, |
| 20854 | + const char *buf, size_t count) |
| 20855 | +{ |
| 20856 | + struct fb_info *fbi = dev_get_drvdata(dev); |
| 20857 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 20858 | + enum omap_dss_rotation_type rot_type; |
| 20859 | + int r; |
| 20860 | + |
| 20861 | + rot_type = simple_strtoul(buf, NULL, 0); |
| 20862 | + |
| 20863 | + if (rot_type != OMAP_DSS_ROT_DMA && rot_type != OMAP_DSS_ROT_VRFB) |
| 20864 | + return -EINVAL; |
| 20865 | + |
| 20866 | + lock_fb_info(fbi); |
| 20867 | + |
| 20868 | + r = 0; |
| 20869 | + if (rot_type == ofbi->rotation_type) |
| 20870 | + goto out; |
| 20871 | + |
| 20872 | + if (ofbi->region.size) { |
| 20873 | + r = -EBUSY; |
| 20874 | + goto out; |
| 20875 | + } |
| 20876 | + |
| 20877 | + ofbi->rotation_type = rot_type; |
| 20878 | + |
| 20879 | + /* |
| 20880 | + * Since the VRAM for this FB is not allocated at the moment we don't |
| 20881 | + * need to do any further parameter checking at this point. |
| 20882 | + */ |
| 20883 | +out: |
| 20884 | + unlock_fb_info(fbi); |
| 20885 | + |
| 20886 | + return r ? r : count; |
| 20887 | +} |
| 20888 | + |
| 20889 | + |
| 20890 | +static ssize_t show_mirror(struct device *dev, |
| 20891 | + struct device_attribute *attr, char *buf) |
| 20892 | +{ |
| 20893 | + struct fb_info *fbi = dev_get_drvdata(dev); |
| 20894 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 20895 | + |
| 20896 | + return snprintf(buf, PAGE_SIZE, "%d\n", ofbi->mirror); |
| 20897 | +} |
| 20898 | + |
| 20899 | +static ssize_t store_mirror(struct device *dev, |
| 20900 | + struct device_attribute *attr, |
| 20901 | + const char *buf, size_t count) |
| 20902 | +{ |
| 20903 | + struct fb_info *fbi = dev_get_drvdata(dev); |
| 20904 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 20905 | + bool mirror; |
| 20906 | + int r; |
| 20907 | + struct fb_var_screeninfo new_var; |
| 20908 | + |
| 20909 | + mirror = simple_strtoul(buf, NULL, 0); |
| 20910 | + |
| 20911 | + if (mirror != 0 && mirror != 1) |
| 20912 | + return -EINVAL; |
| 20913 | + |
| 20914 | + lock_fb_info(fbi); |
| 20915 | + |
| 20916 | + ofbi->mirror = mirror; |
| 20917 | + |
| 20918 | + memcpy(&new_var, &fbi->var, sizeof(new_var)); |
| 20919 | + r = check_fb_var(fbi, &new_var); |
| 20920 | + if (r) |
| 20921 | + goto out; |
| 20922 | + memcpy(&fbi->var, &new_var, sizeof(fbi->var)); |
| 20923 | + |
| 20924 | + set_fb_fix(fbi); |
| 20925 | + |
| 20926 | + r = omapfb_apply_changes(fbi, 0); |
| 20927 | + if (r) |
| 20928 | + goto out; |
| 20929 | + |
| 20930 | + r = count; |
| 20931 | +out: |
| 20932 | + unlock_fb_info(fbi); |
| 20933 | + |
| 20934 | + return r; |
| 20935 | +} |
| 20936 | + |
| 20937 | +static ssize_t show_overlays(struct device *dev, |
| 20938 | + struct device_attribute *attr, char *buf) |
| 20939 | +{ |
| 20940 | + struct fb_info *fbi = dev_get_drvdata(dev); |
| 20941 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 20942 | + struct omapfb2_device *fbdev = ofbi->fbdev; |
| 20943 | + ssize_t l = 0; |
| 20944 | + int t; |
| 20945 | + |
| 20946 | + omapfb_lock(fbdev); |
| 20947 | + lock_fb_info(fbi); |
| 20948 | + |
| 20949 | + for (t = 0; t < ofbi->num_overlays; t++) { |
| 20950 | + struct omap_overlay *ovl = ofbi->overlays[t]; |
| 20951 | + int ovlnum; |
| 20952 | + |
| 20953 | + for (ovlnum = 0; ovlnum < fbdev->num_overlays; ++ovlnum) |
| 20954 | + if (ovl == fbdev->overlays[ovlnum]) |
| 20955 | + break; |
| 20956 | + |
| 20957 | + l += snprintf(buf + l, PAGE_SIZE - l, "%s%d", |
| 20958 | + t == 0 ? "" : ",", ovlnum); |
| 20959 | + } |
| 20960 | + |
| 20961 | + l += snprintf(buf + l, PAGE_SIZE - l, "\n"); |
| 20962 | + |
| 20963 | + unlock_fb_info(fbi); |
| 20964 | + omapfb_unlock(fbdev); |
| 20965 | + |
| 20966 | + return l; |
| 20967 | +} |
| 20968 | + |
| 20969 | +static struct omapfb_info *get_overlay_fb(struct omapfb2_device *fbdev, |
| 20970 | + struct omap_overlay *ovl) |
| 20971 | +{ |
| 20972 | + int i, t; |
| 20973 | + |
| 20974 | + for (i = 0; i < fbdev->num_fbs; i++) { |
| 20975 | + struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); |
| 20976 | + |
| 20977 | + for (t = 0; t < ofbi->num_overlays; t++) { |
| 20978 | + if (ofbi->overlays[t] == ovl) |
| 20979 | + return ofbi; |
| 20980 | + } |
| 20981 | + } |
| 20982 | + |
| 20983 | + return NULL; |
| 20984 | +} |
| 20985 | + |
| 20986 | +static ssize_t store_overlays(struct device *dev, struct device_attribute *attr, |
| 20987 | + const char *buf, size_t count) |
| 20988 | +{ |
| 20989 | + struct fb_info *fbi = dev_get_drvdata(dev); |
| 20990 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 20991 | + struct omapfb2_device *fbdev = ofbi->fbdev; |
| 20992 | + struct omap_overlay *ovls[OMAPFB_MAX_OVL_PER_FB]; |
| 20993 | + struct omap_overlay *ovl; |
| 20994 | + int num_ovls, r, i; |
| 20995 | + int len; |
| 20996 | + bool added = false; |
| 20997 | + |
| 20998 | + num_ovls = 0; |
| 20999 | + |
| 21000 | + len = strlen(buf); |
| 21001 | + if (buf[len - 1] == '\n') |
| 21002 | + len = len - 1; |
| 21003 | + |
| 21004 | + omapfb_lock(fbdev); |
| 21005 | + lock_fb_info(fbi); |
| 21006 | + |
| 21007 | + if (len > 0) { |
| 21008 | + char *p = (char *)buf; |
| 21009 | + int ovlnum; |
| 21010 | + |
| 21011 | + while (p < buf + len) { |
| 21012 | + int found; |
| 21013 | + if (num_ovls == OMAPFB_MAX_OVL_PER_FB) { |
| 21014 | + r = -EINVAL; |
| 21015 | + goto out; |
| 21016 | + } |
| 21017 | + |
| 21018 | + ovlnum = simple_strtoul(p, &p, 0); |
| 21019 | + if (ovlnum > fbdev->num_overlays) { |
| 21020 | + r = -EINVAL; |
| 21021 | + goto out; |
| 21022 | + } |
| 21023 | + |
| 21024 | + found = 0; |
| 21025 | + for (i = 0; i < num_ovls; ++i) { |
| 21026 | + if (ovls[i] == fbdev->overlays[ovlnum]) { |
| 21027 | + found = 1; |
| 21028 | + break; |
| 21029 | + } |
| 21030 | + } |
| 21031 | + |
| 21032 | + if (!found) |
| 21033 | + ovls[num_ovls++] = fbdev->overlays[ovlnum]; |
| 21034 | + |
| 21035 | + p++; |
| 21036 | + } |
| 21037 | + } |
| 21038 | + |
| 21039 | + for (i = 0; i < num_ovls; ++i) { |
| 21040 | + struct omapfb_info *ofbi2 = get_overlay_fb(fbdev, ovls[i]); |
| 21041 | + if (ofbi2 && ofbi2 != ofbi) { |
| 21042 | + dev_err(fbdev->dev, "overlay already in use\n"); |
| 21043 | + r = -EINVAL; |
| 21044 | + goto out; |
| 21045 | + } |
| 21046 | + } |
| 21047 | + |
| 21048 | + /* detach unused overlays */ |
| 21049 | + for (i = 0; i < ofbi->num_overlays; ++i) { |
| 21050 | + int t, found; |
| 21051 | + |
| 21052 | + ovl = ofbi->overlays[i]; |
| 21053 | + |
| 21054 | + found = 0; |
| 21055 | + |
| 21056 | + for (t = 0; t < num_ovls; ++t) { |
| 21057 | + if (ovl == ovls[t]) { |
| 21058 | + found = 1; |
| 21059 | + break; |
| 21060 | + } |
| 21061 | + } |
| 21062 | + |
| 21063 | + if (found) |
| 21064 | + continue; |
| 21065 | + |
| 21066 | + DBG("detaching %d\n", ofbi->overlays[i]->id); |
| 21067 | + |
| 21068 | + omapfb_overlay_enable(ovl, 0); |
| 21069 | + |
| 21070 | + if (ovl->manager) |
| 21071 | + ovl->manager->apply(ovl->manager); |
| 21072 | + |
| 21073 | + for (t = i + 1; t < ofbi->num_overlays; t++) { |
| 21074 | + ofbi->rotation[t-1] = ofbi->rotation[t]; |
| 21075 | + ofbi->overlays[t-1] = ofbi->overlays[t]; |
| 21076 | + } |
| 21077 | + |
| 21078 | + ofbi->num_overlays--; |
| 21079 | + i--; |
| 21080 | + } |
| 21081 | + |
| 21082 | + for (i = 0; i < num_ovls; ++i) { |
| 21083 | + int t, found; |
| 21084 | + |
| 21085 | + ovl = ovls[i]; |
| 21086 | + |
| 21087 | + found = 0; |
| 21088 | + |
| 21089 | + for (t = 0; t < ofbi->num_overlays; ++t) { |
| 21090 | + if (ovl == ofbi->overlays[t]) { |
| 21091 | + found = 1; |
| 21092 | + break; |
| 21093 | + } |
| 21094 | + } |
| 21095 | + |
| 21096 | + if (found) |
| 21097 | + continue; |
| 21098 | + ofbi->rotation[ofbi->num_overlays] = 0; |
| 21099 | + ofbi->overlays[ofbi->num_overlays++] = ovl; |
| 21100 | + |
| 21101 | + added = true; |
| 21102 | + } |
| 21103 | + |
| 21104 | + if (added) { |
| 21105 | + r = omapfb_apply_changes(fbi, 0); |
| 21106 | + if (r) |
| 21107 | + goto out; |
| 21108 | + } |
| 21109 | + |
| 21110 | + r = count; |
| 21111 | +out: |
| 21112 | + unlock_fb_info(fbi); |
| 21113 | + omapfb_unlock(fbdev); |
| 21114 | + |
| 21115 | + return r; |
| 21116 | +} |
| 21117 | + |
| 21118 | +static ssize_t show_overlays_rotate(struct device *dev, |
| 21119 | + struct device_attribute *attr, char *buf) |
| 21120 | +{ |
| 21121 | + struct fb_info *fbi = dev_get_drvdata(dev); |
| 21122 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 21123 | + ssize_t l = 0; |
| 21124 | + int t; |
| 21125 | + |
| 21126 | + lock_fb_info(fbi); |
| 21127 | + |
| 21128 | + for (t = 0; t < ofbi->num_overlays; t++) { |
| 21129 | + l += snprintf(buf + l, PAGE_SIZE - l, "%s%d", |
| 21130 | + t == 0 ? "" : ",", ofbi->rotation[t]); |
| 21131 | + } |
| 21132 | + |
| 21133 | + l += snprintf(buf + l, PAGE_SIZE - l, "\n"); |
| 21134 | + |
| 21135 | + unlock_fb_info(fbi); |
| 21136 | + |
| 21137 | + return l; |
| 21138 | +} |
| 21139 | + |
| 21140 | +static ssize_t store_overlays_rotate(struct device *dev, |
| 21141 | + struct device_attribute *attr, const char *buf, size_t count) |
| 21142 | +{ |
| 21143 | + struct fb_info *fbi = dev_get_drvdata(dev); |
| 21144 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 21145 | + int num_ovls = 0, r, i; |
| 21146 | + int len; |
| 21147 | + bool changed = false; |
| 21148 | + u8 rotation[OMAPFB_MAX_OVL_PER_FB]; |
| 21149 | + |
| 21150 | + len = strlen(buf); |
| 21151 | + if (buf[len - 1] == '\n') |
| 21152 | + len = len - 1; |
| 21153 | + |
| 21154 | + lock_fb_info(fbi); |
| 21155 | + |
| 21156 | + if (len > 0) { |
| 21157 | + char *p = (char *)buf; |
| 21158 | + |
| 21159 | + while (p < buf + len) { |
| 21160 | + int rot; |
| 21161 | + |
| 21162 | + if (num_ovls == ofbi->num_overlays) { |
| 21163 | + r = -EINVAL; |
| 21164 | + goto out; |
| 21165 | + } |
| 21166 | + |
| 21167 | + rot = simple_strtoul(p, &p, 0); |
| 21168 | + if (rot < 0 || rot > 3) { |
| 21169 | + r = -EINVAL; |
| 21170 | + goto out; |
| 21171 | + } |
| 21172 | + |
| 21173 | + if (ofbi->rotation[num_ovls] != rot) |
| 21174 | + changed = true; |
| 21175 | + |
| 21176 | + rotation[num_ovls++] = rot; |
| 21177 | + |
| 21178 | + p++; |
| 21179 | + } |
| 21180 | + } |
| 21181 | + |
| 21182 | + if (num_ovls != ofbi->num_overlays) { |
| 21183 | + r = -EINVAL; |
| 21184 | + goto out; |
| 21185 | + } |
| 21186 | + |
| 21187 | + if (changed) { |
| 21188 | + for (i = 0; i < num_ovls; ++i) |
| 21189 | + ofbi->rotation[i] = rotation[i]; |
| 21190 | + |
| 21191 | + r = omapfb_apply_changes(fbi, 0); |
| 21192 | + if (r) |
| 21193 | + goto out; |
| 21194 | + |
| 21195 | + /* FIXME error handling? */ |
| 21196 | + } |
| 21197 | + |
| 21198 | + r = count; |
| 21199 | +out: |
| 21200 | + unlock_fb_info(fbi); |
| 21201 | + |
| 21202 | + return r; |
| 21203 | +} |
| 21204 | + |
| 21205 | +static ssize_t show_size(struct device *dev, |
| 21206 | + struct device_attribute *attr, char *buf) |
| 21207 | +{ |
| 21208 | + struct fb_info *fbi = dev_get_drvdata(dev); |
| 21209 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 21210 | + |
| 21211 | + return snprintf(buf, PAGE_SIZE, "%lu\n", ofbi->region.size); |
| 21212 | +} |
| 21213 | + |
| 21214 | +static ssize_t store_size(struct device *dev, struct device_attribute *attr, |
| 21215 | + const char *buf, size_t count) |
| 21216 | +{ |
| 21217 | + struct fb_info *fbi = dev_get_drvdata(dev); |
| 21218 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 21219 | + unsigned long size; |
| 21220 | + int r; |
| 21221 | + int i; |
| 21222 | + |
| 21223 | + size = PAGE_ALIGN(simple_strtoul(buf, NULL, 0)); |
| 21224 | + |
| 21225 | + lock_fb_info(fbi); |
| 21226 | + |
| 21227 | + for (i = 0; i < ofbi->num_overlays; i++) { |
| 21228 | + if (ofbi->overlays[i]->info.enabled) { |
| 21229 | + r = -EBUSY; |
| 21230 | + goto out; |
| 21231 | + } |
| 21232 | + } |
| 21233 | + |
| 21234 | + if (size != ofbi->region.size) { |
| 21235 | + r = omapfb_realloc_fbmem(fbi, size, ofbi->region.type); |
| 21236 | + if (r) { |
| 21237 | + dev_err(dev, "realloc fbmem failed\n"); |
| 21238 | + goto out; |
| 21239 | + } |
| 21240 | + } |
| 21241 | + |
| 21242 | + r = count; |
| 21243 | +out: |
| 21244 | + unlock_fb_info(fbi); |
| 21245 | + |
| 21246 | + return r; |
| 21247 | +} |
| 21248 | + |
| 21249 | +static ssize_t show_phys(struct device *dev, |
| 21250 | + struct device_attribute *attr, char *buf) |
| 21251 | +{ |
| 21252 | + struct fb_info *fbi = dev_get_drvdata(dev); |
| 21253 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 21254 | + |
| 21255 | + return snprintf(buf, PAGE_SIZE, "%0x\n", ofbi->region.paddr); |
| 21256 | +} |
| 21257 | + |
| 21258 | +static ssize_t show_virt(struct device *dev, |
| 21259 | + struct device_attribute *attr, char *buf) |
| 21260 | +{ |
| 21261 | + struct fb_info *fbi = dev_get_drvdata(dev); |
| 21262 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 21263 | + |
| 21264 | + return snprintf(buf, PAGE_SIZE, "%p\n", ofbi->region.vaddr); |
| 21265 | +} |
| 21266 | + |
| 21267 | +static struct device_attribute omapfb_attrs[] = { |
| 21268 | + __ATTR(rotate_type, S_IRUGO | S_IWUSR, show_rotate_type, |
| 21269 | + store_rotate_type), |
| 21270 | + __ATTR(mirror, S_IRUGO | S_IWUSR, show_mirror, store_mirror), |
| 21271 | + __ATTR(size, S_IRUGO | S_IWUSR, show_size, store_size), |
| 21272 | + __ATTR(overlays, S_IRUGO | S_IWUSR, show_overlays, store_overlays), |
| 21273 | + __ATTR(overlays_rotate, S_IRUGO | S_IWUSR, show_overlays_rotate, |
| 21274 | + store_overlays_rotate), |
| 21275 | + __ATTR(phys_addr, S_IRUGO, show_phys, NULL), |
| 21276 | + __ATTR(virt_addr, S_IRUGO, show_virt, NULL), |
| 21277 | +}; |
| 21278 | + |
| 21279 | +int omapfb_create_sysfs(struct omapfb2_device *fbdev) |
| 21280 | +{ |
| 21281 | + int i; |
| 21282 | + int r; |
| 21283 | + |
| 21284 | + DBG("create sysfs for fbs\n"); |
| 21285 | + for (i = 0; i < fbdev->num_fbs; i++) { |
| 21286 | + int t; |
| 21287 | + for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++) { |
| 21288 | + r = device_create_file(fbdev->fbs[i]->dev, |
| 21289 | + &omapfb_attrs[t]); |
| 21290 | + |
| 21291 | + if (r) { |
| 21292 | + dev_err(fbdev->dev, "failed to create sysfs " |
| 21293 | + "file\n"); |
| 21294 | + return r; |
| 21295 | + } |
| 21296 | + } |
| 21297 | + } |
| 21298 | + |
| 21299 | + return 0; |
| 21300 | +} |
| 21301 | + |
| 21302 | +void omapfb_remove_sysfs(struct omapfb2_device *fbdev) |
| 21303 | +{ |
| 21304 | + int i, t; |
| 21305 | + |
| 21306 | + DBG("remove sysfs for fbs\n"); |
| 21307 | + for (i = 0; i < fbdev->num_fbs; i++) { |
| 21308 | + for (t = 0; t < ARRAY_SIZE(omapfb_attrs); t++) |
| 21309 | + device_remove_file(fbdev->fbs[i]->dev, |
| 21310 | + &omapfb_attrs[t]); |
| 21311 | + } |
| 21312 | +} |
| 21313 | + |
| 21314 | --- /dev/null |
| 21315 | +++ b/drivers/video/omap2/omapfb/omapfb.h |
| 21316 | @@ -0,0 +1,146 @@ |
| 21317 | +/* |
| 21318 | + * linux/drivers/video/omap2/omapfb.h |
| 21319 | + * |
| 21320 | + * Copyright (C) 2008 Nokia Corporation |
| 21321 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 21322 | + * |
| 21323 | + * Some code and ideas taken from drivers/video/omap/ driver |
| 21324 | + * by Imre Deak. |
| 21325 | + * |
| 21326 | + * This program is free software; you can redistribute it and/or modify it |
| 21327 | + * under the terms of the GNU General Public License version 2 as published by |
| 21328 | + * the Free Software Foundation. |
| 21329 | + * |
| 21330 | + * This program is distributed in the hope that it will be useful, but WITHOUT |
| 21331 | + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 21332 | + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
| 21333 | + * more details. |
| 21334 | + * |
| 21335 | + * You should have received a copy of the GNU General Public License along with |
| 21336 | + * this program. If not, see <http://www.gnu.org/licenses/>. |
| 21337 | + */ |
| 21338 | + |
| 21339 | +#ifndef __DRIVERS_VIDEO_OMAP2_OMAPFB_H__ |
| 21340 | +#define __DRIVERS_VIDEO_OMAP2_OMAPFB_H__ |
| 21341 | + |
| 21342 | +#ifdef CONFIG_FB_OMAP2_DEBUG_SUPPORT |
| 21343 | +#define DEBUG |
| 21344 | +#endif |
| 21345 | + |
| 21346 | +#include <mach/display.h> |
| 21347 | + |
| 21348 | +#ifdef DEBUG |
| 21349 | +extern unsigned int omapfb_debug; |
| 21350 | +#define DBG(format, ...) \ |
| 21351 | + if (omapfb_debug) \ |
| 21352 | + printk(KERN_DEBUG "OMAPFB: " format, ## __VA_ARGS__) |
| 21353 | +#else |
| 21354 | +#define DBG(format, ...) |
| 21355 | +#endif |
| 21356 | + |
| 21357 | +#define FB2OFB(fb_info) ((struct omapfb_info *)(fb_info->par)) |
| 21358 | + |
| 21359 | +/* max number of overlays to which a framebuffer data can be direct */ |
| 21360 | +#define OMAPFB_MAX_OVL_PER_FB 3 |
| 21361 | + |
| 21362 | +struct omapfb2_mem_region { |
| 21363 | + u32 paddr; |
| 21364 | + void __iomem *vaddr; |
| 21365 | + struct vrfb vrfb; |
| 21366 | + unsigned long size; |
| 21367 | + u8 type; /* OMAPFB_PLANE_MEM_* */ |
| 21368 | + bool alloc; /* allocated by the driver */ |
| 21369 | + bool map; /* kernel mapped by the driver */ |
| 21370 | +}; |
| 21371 | + |
| 21372 | +/* appended to fb_info */ |
| 21373 | +struct omapfb_info { |
| 21374 | + int id; |
| 21375 | + struct omapfb2_mem_region region; |
| 21376 | + atomic_t map_count; |
| 21377 | + int num_overlays; |
| 21378 | + struct omap_overlay *overlays[OMAPFB_MAX_OVL_PER_FB]; |
| 21379 | + struct omapfb2_device *fbdev; |
| 21380 | + enum omap_dss_rotation_type rotation_type; |
| 21381 | + u8 rotation[OMAPFB_MAX_OVL_PER_FB]; |
| 21382 | + bool mirror; |
| 21383 | +}; |
| 21384 | + |
| 21385 | +struct omapfb2_device { |
| 21386 | + struct device *dev; |
| 21387 | + struct mutex mtx; |
| 21388 | + |
| 21389 | + u32 pseudo_palette[17]; |
| 21390 | + |
| 21391 | + int state; |
| 21392 | + |
| 21393 | + unsigned num_fbs; |
| 21394 | + struct fb_info *fbs[10]; |
| 21395 | + |
| 21396 | + unsigned num_displays; |
| 21397 | + struct omap_dss_device *displays[10]; |
| 21398 | + unsigned num_overlays; |
| 21399 | + struct omap_overlay *overlays[10]; |
| 21400 | + unsigned num_managers; |
| 21401 | + struct omap_overlay_manager *managers[10]; |
| 21402 | +}; |
| 21403 | + |
| 21404 | +struct omapfb_colormode { |
| 21405 | + enum omap_color_mode dssmode; |
| 21406 | + u32 bits_per_pixel; |
| 21407 | + u32 nonstd; |
| 21408 | + struct fb_bitfield red; |
| 21409 | + struct fb_bitfield green; |
| 21410 | + struct fb_bitfield blue; |
| 21411 | + struct fb_bitfield transp; |
| 21412 | +}; |
| 21413 | + |
| 21414 | +void set_fb_fix(struct fb_info *fbi); |
| 21415 | +int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var); |
| 21416 | +int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type); |
| 21417 | +int omapfb_apply_changes(struct fb_info *fbi, int init); |
| 21418 | + |
| 21419 | +int omapfb_create_sysfs(struct omapfb2_device *fbdev); |
| 21420 | +void omapfb_remove_sysfs(struct omapfb2_device *fbdev); |
| 21421 | + |
| 21422 | +int omapfb_ioctl(struct fb_info *fbi, unsigned int cmd, unsigned long arg); |
| 21423 | + |
| 21424 | +int dss_mode_to_fb_mode(enum omap_color_mode dssmode, |
| 21425 | + struct fb_var_screeninfo *var); |
| 21426 | + |
| 21427 | +/* find the display connected to this fb, if any */ |
| 21428 | +static inline struct omap_dss_device *fb2display(struct fb_info *fbi) |
| 21429 | +{ |
| 21430 | + struct omapfb_info *ofbi = FB2OFB(fbi); |
| 21431 | + int i; |
| 21432 | + |
| 21433 | + /* XXX: returns the display connected to first attached overlay */ |
| 21434 | + for (i = 0; i < ofbi->num_overlays; i++) { |
| 21435 | + if (ofbi->overlays[i]->manager) |
| 21436 | + return ofbi->overlays[i]->manager->device; |
| 21437 | + } |
| 21438 | + |
| 21439 | + return NULL; |
| 21440 | +} |
| 21441 | + |
| 21442 | +static inline void omapfb_lock(struct omapfb2_device *fbdev) |
| 21443 | +{ |
| 21444 | + mutex_lock(&fbdev->mtx); |
| 21445 | +} |
| 21446 | + |
| 21447 | +static inline void omapfb_unlock(struct omapfb2_device *fbdev) |
| 21448 | +{ |
| 21449 | + mutex_unlock(&fbdev->mtx); |
| 21450 | +} |
| 21451 | + |
| 21452 | +static inline int omapfb_overlay_enable(struct omap_overlay *ovl, |
| 21453 | + int enable) |
| 21454 | +{ |
| 21455 | + struct omap_overlay_info info; |
| 21456 | + |
| 21457 | + ovl->get_overlay_info(ovl, &info); |
| 21458 | + info.enabled = enable; |
| 21459 | + return ovl->set_overlay_info(ovl, &info); |
| 21460 | +} |
| 21461 | + |
| 21462 | +#endif |
| 21463 | --- /dev/null |
| 21464 | +++ b/drivers/video/omap2/vram.c |
| 21465 | @@ -0,0 +1,655 @@ |
| 21466 | +/* |
| 21467 | + * VRAM manager for OMAP |
| 21468 | + * |
| 21469 | + * Copyright (C) 2009 Nokia Corporation |
| 21470 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 21471 | + * |
| 21472 | + * This program is free software; you can redistribute it and/or modify |
| 21473 | + * it under the terms of the GNU General Public License version 2 as |
| 21474 | + * published by the Free Software Foundation. |
| 21475 | + * |
| 21476 | + * This program is distributed in the hope that it will be useful, but |
| 21477 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 21478 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 21479 | + * General Public License for more details. |
| 21480 | + * |
| 21481 | + * You should have received a copy of the GNU General Public License along |
| 21482 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 21483 | + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 21484 | + */ |
| 21485 | + |
| 21486 | +/*#define DEBUG*/ |
| 21487 | + |
| 21488 | +#include <linux/kernel.h> |
| 21489 | +#include <linux/mm.h> |
| 21490 | +#include <linux/list.h> |
| 21491 | +#include <linux/seq_file.h> |
| 21492 | +#include <linux/bootmem.h> |
| 21493 | +#include <linux/completion.h> |
| 21494 | +#include <linux/debugfs.h> |
| 21495 | +#include <linux/jiffies.h> |
| 21496 | +#include <linux/module.h> |
| 21497 | + |
| 21498 | +#include <asm/setup.h> |
| 21499 | + |
| 21500 | +#include <mach/sram.h> |
| 21501 | +#include <mach/vram.h> |
| 21502 | +#include <mach/dma.h> |
| 21503 | + |
| 21504 | +#ifdef DEBUG |
| 21505 | +#define DBG(format, ...) pr_debug("VRAM: " format, ## __VA_ARGS__) |
| 21506 | +#else |
| 21507 | +#define DBG(format, ...) |
| 21508 | +#endif |
| 21509 | + |
| 21510 | +#define OMAP2_SRAM_START 0x40200000 |
| 21511 | +/* Maximum size, in reality this is smaller if SRAM is partially locked. */ |
| 21512 | +#define OMAP2_SRAM_SIZE 0xa0000 /* 640k */ |
| 21513 | + |
| 21514 | +/* postponed regions are used to temporarily store region information at boot |
| 21515 | + * time when we cannot yet allocate the region list */ |
| 21516 | +#define MAX_POSTPONED_REGIONS 10 |
| 21517 | + |
| 21518 | +static bool vram_initialized; |
| 21519 | +static int postponed_cnt; |
| 21520 | +static struct { |
| 21521 | + unsigned long paddr; |
| 21522 | + size_t size; |
| 21523 | +} postponed_regions[MAX_POSTPONED_REGIONS]; |
| 21524 | + |
| 21525 | +struct vram_alloc { |
| 21526 | + struct list_head list; |
| 21527 | + unsigned long paddr; |
| 21528 | + unsigned pages; |
| 21529 | +}; |
| 21530 | + |
| 21531 | +struct vram_region { |
| 21532 | + struct list_head list; |
| 21533 | + struct list_head alloc_list; |
| 21534 | + unsigned long paddr; |
| 21535 | + unsigned pages; |
| 21536 | +}; |
| 21537 | + |
| 21538 | +static DEFINE_MUTEX(region_mutex); |
| 21539 | +static LIST_HEAD(region_list); |
| 21540 | + |
| 21541 | +static inline int region_mem_type(unsigned long paddr) |
| 21542 | +{ |
| 21543 | + if (paddr >= OMAP2_SRAM_START && |
| 21544 | + paddr < OMAP2_SRAM_START + OMAP2_SRAM_SIZE) |
| 21545 | + return OMAP_VRAM_MEMTYPE_SRAM; |
| 21546 | + else |
| 21547 | + return OMAP_VRAM_MEMTYPE_SDRAM; |
| 21548 | +} |
| 21549 | + |
| 21550 | +static struct vram_region *omap_vram_create_region(unsigned long paddr, |
| 21551 | + unsigned pages) |
| 21552 | +{ |
| 21553 | + struct vram_region *rm; |
| 21554 | + |
| 21555 | + rm = kzalloc(sizeof(*rm), GFP_KERNEL); |
| 21556 | + |
| 21557 | + if (rm) { |
| 21558 | + INIT_LIST_HEAD(&rm->alloc_list); |
| 21559 | + rm->paddr = paddr; |
| 21560 | + rm->pages = pages; |
| 21561 | + } |
| 21562 | + |
| 21563 | + return rm; |
| 21564 | +} |
| 21565 | + |
| 21566 | +#if 0 |
| 21567 | +static void omap_vram_free_region(struct vram_region *vr) |
| 21568 | +{ |
| 21569 | + list_del(&vr->list); |
| 21570 | + kfree(vr); |
| 21571 | +} |
| 21572 | +#endif |
| 21573 | + |
| 21574 | +static struct vram_alloc *omap_vram_create_allocation(struct vram_region *vr, |
| 21575 | + unsigned long paddr, unsigned pages) |
| 21576 | +{ |
| 21577 | + struct vram_alloc *va; |
| 21578 | + struct vram_alloc *new; |
| 21579 | + |
| 21580 | + new = kzalloc(sizeof(*va), GFP_KERNEL); |
| 21581 | + |
| 21582 | + if (!new) |
| 21583 | + return NULL; |
| 21584 | + |
| 21585 | + new->paddr = paddr; |
| 21586 | + new->pages = pages; |
| 21587 | + |
| 21588 | + list_for_each_entry(va, &vr->alloc_list, list) { |
| 21589 | + if (va->paddr > new->paddr) |
| 21590 | + break; |
| 21591 | + } |
| 21592 | + |
| 21593 | + list_add_tail(&new->list, &va->list); |
| 21594 | + |
| 21595 | + return new; |
| 21596 | +} |
| 21597 | + |
| 21598 | +static void omap_vram_free_allocation(struct vram_alloc *va) |
| 21599 | +{ |
| 21600 | + list_del(&va->list); |
| 21601 | + kfree(va); |
| 21602 | +} |
| 21603 | + |
| 21604 | +int omap_vram_add_region(unsigned long paddr, size_t size) |
| 21605 | +{ |
| 21606 | + struct vram_region *rm; |
| 21607 | + unsigned pages; |
| 21608 | + |
| 21609 | + if (vram_initialized) { |
| 21610 | + DBG("adding region paddr %08lx size %d\n", |
| 21611 | + paddr, size); |
| 21612 | + |
| 21613 | + size &= PAGE_MASK; |
| 21614 | + pages = size >> PAGE_SHIFT; |
| 21615 | + |
| 21616 | + rm = omap_vram_create_region(paddr, pages); |
| 21617 | + if (rm == NULL) |
| 21618 | + return -ENOMEM; |
| 21619 | + |
| 21620 | + list_add(&rm->list, ®ion_list); |
| 21621 | + } else { |
| 21622 | + if (postponed_cnt == MAX_POSTPONED_REGIONS) |
| 21623 | + return -ENOMEM; |
| 21624 | + |
| 21625 | + postponed_regions[postponed_cnt].paddr = paddr; |
| 21626 | + postponed_regions[postponed_cnt].size = size; |
| 21627 | + |
| 21628 | + ++postponed_cnt; |
| 21629 | + } |
| 21630 | + return 0; |
| 21631 | +} |
| 21632 | + |
| 21633 | +int omap_vram_free(unsigned long paddr, size_t size) |
| 21634 | +{ |
| 21635 | + struct vram_region *rm; |
| 21636 | + struct vram_alloc *alloc; |
| 21637 | + unsigned start, end; |
| 21638 | + |
| 21639 | + DBG("free mem paddr %08lx size %d\n", paddr, size); |
| 21640 | + |
| 21641 | + size = PAGE_ALIGN(size); |
| 21642 | + |
| 21643 | + mutex_lock(®ion_mutex); |
| 21644 | + |
| 21645 | + list_for_each_entry(rm, ®ion_list, list) { |
| 21646 | + list_for_each_entry(alloc, &rm->alloc_list, list) { |
| 21647 | + start = alloc->paddr; |
| 21648 | + end = alloc->paddr + (alloc->pages >> PAGE_SHIFT); |
| 21649 | + |
| 21650 | + if (start >= paddr && end < paddr + size) |
| 21651 | + goto found; |
| 21652 | + } |
| 21653 | + } |
| 21654 | + |
| 21655 | + mutex_unlock(®ion_mutex); |
| 21656 | + return -EINVAL; |
| 21657 | + |
| 21658 | +found: |
| 21659 | + omap_vram_free_allocation(alloc); |
| 21660 | + |
| 21661 | + mutex_unlock(®ion_mutex); |
| 21662 | + return 0; |
| 21663 | +} |
| 21664 | +EXPORT_SYMBOL(omap_vram_free); |
| 21665 | + |
| 21666 | +static int _omap_vram_reserve(unsigned long paddr, unsigned pages) |
| 21667 | +{ |
| 21668 | + struct vram_region *rm; |
| 21669 | + struct vram_alloc *alloc; |
| 21670 | + size_t size; |
| 21671 | + |
| 21672 | + size = pages << PAGE_SHIFT; |
| 21673 | + |
| 21674 | + list_for_each_entry(rm, ®ion_list, list) { |
| 21675 | + unsigned long start, end; |
| 21676 | + |
| 21677 | + DBG("checking region %lx %d\n", rm->paddr, rm->pages); |
| 21678 | + |
| 21679 | + if (region_mem_type(rm->paddr) != region_mem_type(paddr)) |
| 21680 | + continue; |
| 21681 | + |
| 21682 | + start = rm->paddr; |
| 21683 | + end = start + (rm->pages << PAGE_SHIFT) - 1; |
| 21684 | + if (start > paddr || end < paddr + size - 1) |
| 21685 | + continue; |
| 21686 | + |
| 21687 | + DBG("block ok, checking allocs\n"); |
| 21688 | + |
| 21689 | + list_for_each_entry(alloc, &rm->alloc_list, list) { |
| 21690 | + end = alloc->paddr - 1; |
| 21691 | + |
| 21692 | + if (start <= paddr && end >= paddr + size - 1) |
| 21693 | + goto found; |
| 21694 | + |
| 21695 | + start = alloc->paddr + (alloc->pages << PAGE_SHIFT); |
| 21696 | + } |
| 21697 | + |
| 21698 | + end = rm->paddr + (rm->pages << PAGE_SHIFT) - 1; |
| 21699 | + |
| 21700 | + if (!(start <= paddr && end >= paddr + size - 1)) |
| 21701 | + continue; |
| 21702 | +found: |
| 21703 | + DBG("found area start %lx, end %lx\n", start, end); |
| 21704 | + |
| 21705 | + if (omap_vram_create_allocation(rm, paddr, pages) == NULL) |
| 21706 | + return -ENOMEM; |
| 21707 | + |
| 21708 | + return 0; |
| 21709 | + } |
| 21710 | + |
| 21711 | + return -ENOMEM; |
| 21712 | +} |
| 21713 | + |
| 21714 | +int omap_vram_reserve(unsigned long paddr, size_t size) |
| 21715 | +{ |
| 21716 | + unsigned pages; |
| 21717 | + int r; |
| 21718 | + |
| 21719 | + DBG("reserve mem paddr %08lx size %d\n", paddr, size); |
| 21720 | + |
| 21721 | + size = PAGE_ALIGN(size); |
| 21722 | + pages = size >> PAGE_SHIFT; |
| 21723 | + |
| 21724 | + mutex_lock(®ion_mutex); |
| 21725 | + |
| 21726 | + r = _omap_vram_reserve(paddr, pages); |
| 21727 | + |
| 21728 | + mutex_unlock(®ion_mutex); |
| 21729 | + |
| 21730 | + return r; |
| 21731 | +} |
| 21732 | +EXPORT_SYMBOL(omap_vram_reserve); |
| 21733 | + |
| 21734 | +static void _omap_vram_dma_cb(int lch, u16 ch_status, void *data) |
| 21735 | +{ |
| 21736 | + struct completion *compl = data; |
| 21737 | + complete(compl); |
| 21738 | +} |
| 21739 | + |
| 21740 | +static int _omap_vram_clear(u32 paddr, unsigned pages) |
| 21741 | +{ |
| 21742 | + struct completion compl; |
| 21743 | + unsigned elem_count; |
| 21744 | + unsigned frame_count; |
| 21745 | + int r; |
| 21746 | + int lch; |
| 21747 | + |
| 21748 | + init_completion(&compl); |
| 21749 | + |
| 21750 | + r = omap_request_dma(OMAP_DMA_NO_DEVICE, "VRAM DMA", |
| 21751 | + _omap_vram_dma_cb, |
| 21752 | + &compl, &lch); |
| 21753 | + if (r) { |
| 21754 | + pr_err("VRAM: request_dma failed for memory clear\n"); |
| 21755 | + return -EBUSY; |
| 21756 | + } |
| 21757 | + |
| 21758 | + elem_count = pages * PAGE_SIZE / 4; |
| 21759 | + frame_count = 1; |
| 21760 | + |
| 21761 | + omap_set_dma_transfer_params(lch, OMAP_DMA_DATA_TYPE_S32, |
| 21762 | + elem_count, frame_count, |
| 21763 | + OMAP_DMA_SYNC_ELEMENT, |
| 21764 | + 0, 0); |
| 21765 | + |
| 21766 | + omap_set_dma_dest_params(lch, 0, OMAP_DMA_AMODE_POST_INC, |
| 21767 | + paddr, 0, 0); |
| 21768 | + |
| 21769 | + omap_set_dma_color_mode(lch, OMAP_DMA_CONSTANT_FILL, 0x000000); |
| 21770 | + |
| 21771 | + omap_start_dma(lch); |
| 21772 | + |
| 21773 | + if (wait_for_completion_timeout(&compl, msecs_to_jiffies(1000)) == 0) { |
| 21774 | + omap_stop_dma(lch); |
| 21775 | + pr_err("VRAM: dma timeout while clearing memory\n"); |
| 21776 | + r = -EIO; |
| 21777 | + goto err; |
| 21778 | + } |
| 21779 | + |
| 21780 | + r = 0; |
| 21781 | +err: |
| 21782 | + omap_free_dma(lch); |
| 21783 | + |
| 21784 | + return r; |
| 21785 | +} |
| 21786 | + |
| 21787 | +static int _omap_vram_alloc(int mtype, unsigned pages, unsigned long *paddr) |
| 21788 | +{ |
| 21789 | + struct vram_region *rm; |
| 21790 | + struct vram_alloc *alloc; |
| 21791 | + |
| 21792 | + list_for_each_entry(rm, ®ion_list, list) { |
| 21793 | + unsigned long start, end; |
| 21794 | + |
| 21795 | + DBG("checking region %lx %d\n", rm->paddr, rm->pages); |
| 21796 | + |
| 21797 | + if (region_mem_type(rm->paddr) != mtype) |
| 21798 | + continue; |
| 21799 | + |
| 21800 | + start = rm->paddr; |
| 21801 | + |
| 21802 | + list_for_each_entry(alloc, &rm->alloc_list, list) { |
| 21803 | + end = alloc->paddr; |
| 21804 | + |
| 21805 | + if (end - start >= pages << PAGE_SHIFT) |
| 21806 | + goto found; |
| 21807 | + |
| 21808 | + start = alloc->paddr + (alloc->pages << PAGE_SHIFT); |
| 21809 | + } |
| 21810 | + |
| 21811 | + end = rm->paddr + (rm->pages << PAGE_SHIFT); |
| 21812 | +found: |
| 21813 | + if (end - start < pages << PAGE_SHIFT) |
| 21814 | + continue; |
| 21815 | + |
| 21816 | + DBG("found %lx, end %lx\n", start, end); |
| 21817 | + |
| 21818 | + alloc = omap_vram_create_allocation(rm, start, pages); |
| 21819 | + if (alloc == NULL) |
| 21820 | + return -ENOMEM; |
| 21821 | + |
| 21822 | + *paddr = start; |
| 21823 | + |
| 21824 | + _omap_vram_clear(start, pages); |
| 21825 | + |
| 21826 | + return 0; |
| 21827 | + } |
| 21828 | + |
| 21829 | + return -ENOMEM; |
| 21830 | +} |
| 21831 | + |
| 21832 | +int omap_vram_alloc(int mtype, size_t size, unsigned long *paddr) |
| 21833 | +{ |
| 21834 | + unsigned pages; |
| 21835 | + int r; |
| 21836 | + |
| 21837 | + BUG_ON(mtype > OMAP_VRAM_MEMTYPE_MAX || !size); |
| 21838 | + |
| 21839 | + DBG("alloc mem type %d size %d\n", mtype, size); |
| 21840 | + |
| 21841 | + size = PAGE_ALIGN(size); |
| 21842 | + pages = size >> PAGE_SHIFT; |
| 21843 | + |
| 21844 | + mutex_lock(®ion_mutex); |
| 21845 | + |
| 21846 | + r = _omap_vram_alloc(mtype, pages, paddr); |
| 21847 | + |
| 21848 | + mutex_unlock(®ion_mutex); |
| 21849 | + |
| 21850 | + return r; |
| 21851 | +} |
| 21852 | +EXPORT_SYMBOL(omap_vram_alloc); |
| 21853 | + |
| 21854 | +void omap_vram_get_info(unsigned long *vram, |
| 21855 | + unsigned long *free_vram, |
| 21856 | + unsigned long *largest_free_block) |
| 21857 | +{ |
| 21858 | + struct vram_region *vr; |
| 21859 | + struct vram_alloc *va; |
| 21860 | + |
| 21861 | + *vram = 0; |
| 21862 | + *free_vram = 0; |
| 21863 | + *largest_free_block = 0; |
| 21864 | + |
| 21865 | + mutex_lock(®ion_mutex); |
| 21866 | + |
| 21867 | + list_for_each_entry(vr, ®ion_list, list) { |
| 21868 | + unsigned free; |
| 21869 | + unsigned long pa; |
| 21870 | + |
| 21871 | + pa = vr->paddr; |
| 21872 | + *vram += vr->pages << PAGE_SHIFT; |
| 21873 | + |
| 21874 | + list_for_each_entry(va, &vr->alloc_list, list) { |
| 21875 | + free = va->paddr - pa; |
| 21876 | + *free_vram += free; |
| 21877 | + if (free > *largest_free_block) |
| 21878 | + *largest_free_block = free; |
| 21879 | + pa = va->paddr + (va->pages << PAGE_SHIFT); |
| 21880 | + } |
| 21881 | + |
| 21882 | + free = vr->paddr + (vr->pages << PAGE_SHIFT) - pa; |
| 21883 | + *free_vram += free; |
| 21884 | + if (free > *largest_free_block) |
| 21885 | + *largest_free_block = free; |
| 21886 | + } |
| 21887 | + |
| 21888 | + mutex_unlock(®ion_mutex); |
| 21889 | +} |
| 21890 | +EXPORT_SYMBOL(omap_vram_get_info); |
| 21891 | + |
| 21892 | +#if defined(CONFIG_DEBUG_FS) |
| 21893 | +static int vram_debug_show(struct seq_file *s, void *unused) |
| 21894 | +{ |
| 21895 | + struct vram_region *vr; |
| 21896 | + struct vram_alloc *va; |
| 21897 | + unsigned size; |
| 21898 | + |
| 21899 | + mutex_lock(®ion_mutex); |
| 21900 | + |
| 21901 | + list_for_each_entry(vr, ®ion_list, list) { |
| 21902 | + size = vr->pages << PAGE_SHIFT; |
| 21903 | + seq_printf(s, "%08lx-%08lx (%d bytes)\n", |
| 21904 | + vr->paddr, vr->paddr + size - 1, |
| 21905 | + size); |
| 21906 | + |
| 21907 | + list_for_each_entry(va, &vr->alloc_list, list) { |
| 21908 | + size = va->pages << PAGE_SHIFT; |
| 21909 | + seq_printf(s, " %08lx-%08lx (%d bytes)\n", |
| 21910 | + va->paddr, va->paddr + size - 1, |
| 21911 | + size); |
| 21912 | + } |
| 21913 | + } |
| 21914 | + |
| 21915 | + mutex_unlock(®ion_mutex); |
| 21916 | + |
| 21917 | + return 0; |
| 21918 | +} |
| 21919 | + |
| 21920 | +static int vram_debug_open(struct inode *inode, struct file *file) |
| 21921 | +{ |
| 21922 | + return single_open(file, vram_debug_show, inode->i_private); |
| 21923 | +} |
| 21924 | + |
| 21925 | +static const struct file_operations vram_debug_fops = { |
| 21926 | + .open = vram_debug_open, |
| 21927 | + .read = seq_read, |
| 21928 | + .llseek = seq_lseek, |
| 21929 | + .release = single_release, |
| 21930 | +}; |
| 21931 | + |
| 21932 | +static int __init omap_vram_create_debugfs(void) |
| 21933 | +{ |
| 21934 | + struct dentry *d; |
| 21935 | + |
| 21936 | + d = debugfs_create_file("vram", S_IRUGO, NULL, |
| 21937 | + NULL, &vram_debug_fops); |
| 21938 | + if (IS_ERR(d)) |
| 21939 | + return PTR_ERR(d); |
| 21940 | + |
| 21941 | + return 0; |
| 21942 | +} |
| 21943 | +#endif |
| 21944 | + |
| 21945 | +static __init int omap_vram_init(void) |
| 21946 | +{ |
| 21947 | + int i; |
| 21948 | + |
| 21949 | + vram_initialized = 1; |
| 21950 | + |
| 21951 | + for (i = 0; i < postponed_cnt; i++) |
| 21952 | + omap_vram_add_region(postponed_regions[i].paddr, |
| 21953 | + postponed_regions[i].size); |
| 21954 | + |
| 21955 | +#ifdef CONFIG_DEBUG_FS |
| 21956 | + if (omap_vram_create_debugfs()) |
| 21957 | + pr_err("VRAM: Failed to create debugfs file\n"); |
| 21958 | +#endif |
| 21959 | + |
| 21960 | + return 0; |
| 21961 | +} |
| 21962 | + |
| 21963 | +arch_initcall(omap_vram_init); |
| 21964 | + |
| 21965 | +/* boottime vram alloc stuff */ |
| 21966 | + |
| 21967 | +/* set from board file */ |
| 21968 | +static u32 omap_vram_sram_start __initdata; |
| 21969 | +static u32 omap_vram_sram_size __initdata; |
| 21970 | + |
| 21971 | +/* set from board file */ |
| 21972 | +static u32 omap_vram_sdram_start __initdata; |
| 21973 | +static u32 omap_vram_sdram_size __initdata; |
| 21974 | + |
| 21975 | +/* set from kernel cmdline */ |
| 21976 | +static u32 omap_vram_def_sdram_size __initdata; |
| 21977 | +static u32 omap_vram_def_sdram_start __initdata; |
| 21978 | + |
| 21979 | +static void __init omap_vram_early_vram(char **p) |
| 21980 | +{ |
| 21981 | + omap_vram_def_sdram_size = memparse(*p, p); |
| 21982 | + if (**p == ',') |
| 21983 | + omap_vram_def_sdram_start = simple_strtoul((*p) + 1, p, 16); |
| 21984 | +} |
| 21985 | +__early_param("vram=", omap_vram_early_vram); |
| 21986 | + |
| 21987 | +/* |
| 21988 | + * Called from map_io. We need to call to this early enough so that we |
| 21989 | + * can reserve the fixed SDRAM regions before VM could get hold of them. |
| 21990 | + */ |
| 21991 | +void __init omap_vram_reserve_sdram(void) |
| 21992 | +{ |
| 21993 | + struct bootmem_data *bdata; |
| 21994 | + unsigned long sdram_start, sdram_size; |
| 21995 | + u32 paddr; |
| 21996 | + u32 size = 0; |
| 21997 | + |
| 21998 | + /* cmdline arg overrides the board file definition */ |
| 21999 | + if (omap_vram_def_sdram_size) { |
| 22000 | + size = omap_vram_def_sdram_size; |
| 22001 | + paddr = omap_vram_def_sdram_start; |
| 22002 | + } |
| 22003 | + |
| 22004 | + if (!size) { |
| 22005 | + size = omap_vram_sdram_size; |
| 22006 | + paddr = omap_vram_sdram_start; |
| 22007 | + } |
| 22008 | + |
| 22009 | +#ifdef CONFIG_OMAP2_VRAM_SIZE |
| 22010 | + if (!size) { |
| 22011 | + size = CONFIG_OMAP2_VRAM_SIZE * 1024 * 1024; |
| 22012 | + paddr = 0; |
| 22013 | + } |
| 22014 | +#endif |
| 22015 | + |
| 22016 | + if (!size) |
| 22017 | + return; |
| 22018 | + |
| 22019 | + size = PAGE_ALIGN(size); |
| 22020 | + |
| 22021 | + bdata = NODE_DATA(0)->bdata; |
| 22022 | + sdram_start = bdata->node_min_pfn << PAGE_SHIFT; |
| 22023 | + sdram_size = (bdata->node_low_pfn << PAGE_SHIFT) - sdram_start; |
| 22024 | + |
| 22025 | + if (paddr) { |
| 22026 | + if ((paddr & ~PAGE_MASK) || paddr < sdram_start || |
| 22027 | + paddr + size > sdram_start + sdram_size) { |
| 22028 | + pr_err("Illegal SDRAM region for VRAM\n"); |
| 22029 | + return; |
| 22030 | + } |
| 22031 | + |
| 22032 | + if (reserve_bootmem(paddr, size, BOOTMEM_EXCLUSIVE) < 0) { |
| 22033 | + pr_err("FB: failed to reserve VRAM\n"); |
| 22034 | + return; |
| 22035 | + } |
| 22036 | + } else { |
| 22037 | + if (size > sdram_size) { |
| 22038 | + pr_err("Illegal SDRAM size for VRAM\n"); |
| 22039 | + return; |
| 22040 | + } |
| 22041 | + |
| 22042 | + paddr = virt_to_phys(alloc_bootmem_pages(size)); |
| 22043 | + BUG_ON(paddr & ~PAGE_MASK); |
| 22044 | + } |
| 22045 | + |
| 22046 | + omap_vram_add_region(paddr, size); |
| 22047 | + |
| 22048 | + pr_info("Reserving %u bytes SDRAM for VRAM\n", size); |
| 22049 | +} |
| 22050 | + |
| 22051 | +/* |
| 22052 | + * Called at sram init time, before anything is pushed to the SRAM stack. |
| 22053 | + * Because of the stack scheme, we will allocate everything from the |
| 22054 | + * start of the lowest address region to the end of SRAM. This will also |
| 22055 | + * include padding for page alignment and possible holes between regions. |
| 22056 | + * |
| 22057 | + * As opposed to the SDRAM case, we'll also do any dynamic allocations at |
| 22058 | + * this point, since the driver built as a module would have problem with |
| 22059 | + * freeing / reallocating the regions. |
| 22060 | + */ |
| 22061 | +unsigned long __init omap_vram_reserve_sram(unsigned long sram_pstart, |
| 22062 | + unsigned long sram_vstart, |
| 22063 | + unsigned long sram_size, |
| 22064 | + unsigned long pstart_avail, |
| 22065 | + unsigned long size_avail) |
| 22066 | +{ |
| 22067 | + unsigned long pend_avail; |
| 22068 | + unsigned long reserved; |
| 22069 | + u32 paddr; |
| 22070 | + u32 size; |
| 22071 | + |
| 22072 | + paddr = omap_vram_sram_start; |
| 22073 | + size = omap_vram_sram_size; |
| 22074 | + |
| 22075 | + if (!size) |
| 22076 | + return 0; |
| 22077 | + |
| 22078 | + reserved = 0; |
| 22079 | + pend_avail = pstart_avail + size_avail; |
| 22080 | + |
| 22081 | + if (!paddr) { |
| 22082 | + /* Dynamic allocation */ |
| 22083 | + if ((size_avail & PAGE_MASK) < size) { |
| 22084 | + pr_err("Not enough SRAM for VRAM\n"); |
| 22085 | + return 0; |
| 22086 | + } |
| 22087 | + size_avail = (size_avail - size) & PAGE_MASK; |
| 22088 | + paddr = pstart_avail + size_avail; |
| 22089 | + } |
| 22090 | + |
| 22091 | + if (paddr < sram_pstart || |
| 22092 | + paddr + size > sram_pstart + sram_size) { |
| 22093 | + pr_err("Illegal SRAM region for VRAM\n"); |
| 22094 | + return 0; |
| 22095 | + } |
| 22096 | + |
| 22097 | + /* Reserve everything above the start of the region. */ |
| 22098 | + if (pend_avail - paddr > reserved) |
| 22099 | + reserved = pend_avail - paddr; |
| 22100 | + size_avail = pend_avail - reserved - pstart_avail; |
| 22101 | + |
| 22102 | + omap_vram_add_region(paddr, size); |
| 22103 | + |
| 22104 | + if (reserved) |
| 22105 | + pr_info("Reserving %lu bytes SRAM for VRAM\n", reserved); |
| 22106 | + |
| 22107 | + return reserved; |
| 22108 | +} |
| 22109 | + |
| 22110 | +void __init omap_vram_set_sdram_vram(u32 size, u32 start) |
| 22111 | +{ |
| 22112 | + omap_vram_sdram_start = start; |
| 22113 | + omap_vram_sdram_size = size; |
| 22114 | +} |
| 22115 | + |
| 22116 | +void __init omap_vram_set_sram_vram(u32 size, u32 start) |
| 22117 | +{ |
| 22118 | + omap_vram_sram_start = start; |
| 22119 | + omap_vram_sram_size = size; |
| 22120 | +} |
| 22121 | --- /dev/null |
| 22122 | +++ b/drivers/video/omap2/vrfb.c |
| 22123 | @@ -0,0 +1,277 @@ |
| 22124 | +/* |
| 22125 | + * VRFB Rotation Engine |
| 22126 | + * |
| 22127 | + * Copyright (C) 2009 Nokia Corporation |
| 22128 | + * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
| 22129 | + * |
| 22130 | + * This program is free software; you can redistribute it and/or modify |
| 22131 | + * it under the terms of the GNU General Public License version 2 as |
| 22132 | + * published by the Free Software Foundation. |
| 22133 | + * |
| 22134 | + * This program is distributed in the hope that it will be useful, but |
| 22135 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22136 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 22137 | + * General Public License for more details. |
| 22138 | + * |
| 22139 | + * You should have received a copy of the GNU General Public License along |
| 22140 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 22141 | + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 22142 | + */ |
| 22143 | + |
| 22144 | +#include <linux/kernel.h> |
| 22145 | +#include <linux/module.h> |
| 22146 | +#include <linux/ioport.h> |
| 22147 | +#include <linux/io.h> |
| 22148 | +#include <linux/bitops.h> |
| 22149 | +#include <linux/mutex.h> |
| 22150 | + |
| 22151 | +#include <mach/io.h> |
| 22152 | +#include <mach/vrfb.h> |
| 22153 | +#include <mach/sdrc.h> |
| 22154 | +/*#define DEBUG*/ |
| 22155 | + |
| 22156 | +#ifdef DEBUG |
| 22157 | +#define DBG(format, ...) pr_debug("VRFB: " format, ## __VA_ARGS__) |
| 22158 | +#else |
| 22159 | +#define DBG(format, ...) |
| 22160 | +#endif |
| 22161 | + |
| 22162 | +#define SMS_ROT_VIRT_BASE(context, rot) \ |
| 22163 | + (((context >= 4) ? 0xD0000000 : 0x70000000) \ |
| 22164 | + + (0x4000000 * (context)) \ |
| 22165 | + + (0x1000000 * (rot))) |
| 22166 | + |
| 22167 | +#define OMAP_VRFB_SIZE (2048 * 2048 * 4) |
| 22168 | + |
| 22169 | +#define VRFB_PAGE_WIDTH_EXP 5 /* Assuming SDRAM pagesize= 1024 */ |
| 22170 | +#define VRFB_PAGE_HEIGHT_EXP 5 /* 1024 = 2^5 * 2^5 */ |
| 22171 | +#define VRFB_PAGE_WIDTH (1 << VRFB_PAGE_WIDTH_EXP) |
| 22172 | +#define VRFB_PAGE_HEIGHT (1 << VRFB_PAGE_HEIGHT_EXP) |
| 22173 | +#define SMS_IMAGEHEIGHT_OFFSET 16 |
| 22174 | +#define SMS_IMAGEWIDTH_OFFSET 0 |
| 22175 | +#define SMS_PH_OFFSET 8 |
| 22176 | +#define SMS_PW_OFFSET 4 |
| 22177 | +#define SMS_PS_OFFSET 0 |
| 22178 | + |
| 22179 | +#define VRFB_NUM_CTXS 12 |
| 22180 | +/* bitmap of reserved contexts */ |
| 22181 | +static unsigned long ctx_map; |
| 22182 | +/* bitmap of contexts for which we have to keep the HW context valid */ |
| 22183 | +static unsigned long ctx_map_active; |
| 22184 | + |
| 22185 | +static DEFINE_MUTEX(ctx_lock); |
| 22186 | + |
| 22187 | +/* |
| 22188 | + * Access to this happens from client drivers or the PM core after wake-up. |
| 22189 | + * For the first case we require locking at the driver level, for the second |
| 22190 | + * we don't need locking, since no drivers will run until after the wake-up |
| 22191 | + * has finished. |
| 22192 | + */ |
| 22193 | +static struct { |
| 22194 | + u32 physical_ba; |
| 22195 | + u32 control; |
| 22196 | + u32 size; |
| 22197 | +} vrfb_hw_context[VRFB_NUM_CTXS]; |
| 22198 | + |
| 22199 | +static inline void restore_hw_context(int ctx) |
| 22200 | +{ |
| 22201 | + omap2_sms_write_rot_control(vrfb_hw_context[ctx].control, ctx); |
| 22202 | + omap2_sms_write_rot_size(vrfb_hw_context[ctx].size, ctx); |
| 22203 | + omap2_sms_write_rot_physical_ba(vrfb_hw_context[ctx].physical_ba, ctx); |
| 22204 | +} |
| 22205 | + |
| 22206 | +void omap_vrfb_restore_context(void) |
| 22207 | +{ |
| 22208 | + int i; |
| 22209 | + unsigned long map = ctx_map_active; |
| 22210 | + |
| 22211 | + for (i = ffs(map); i; i = ffs(map)) { |
| 22212 | + /* i=1..32 */ |
| 22213 | + i--; |
| 22214 | + map &= ~(1 << i); |
| 22215 | + restore_hw_context(i); |
| 22216 | + } |
| 22217 | +} |
| 22218 | + |
| 22219 | +void omap_vrfb_adjust_size(u16 *width, u16 *height, |
| 22220 | + u8 bytespp) |
| 22221 | +{ |
| 22222 | + *width = ALIGN(*width * bytespp, VRFB_PAGE_WIDTH) / bytespp; |
| 22223 | + *height = ALIGN(*height, VRFB_PAGE_HEIGHT); |
| 22224 | +} |
| 22225 | +EXPORT_SYMBOL(omap_vrfb_adjust_size); |
| 22226 | + |
| 22227 | +void omap_vrfb_setup(struct vrfb *vrfb, unsigned long paddr, |
| 22228 | + u16 width, u16 height, |
| 22229 | + unsigned bytespp, bool yuv_mode) |
| 22230 | +{ |
| 22231 | + unsigned pixel_size_exp; |
| 22232 | + u16 vrfb_width; |
| 22233 | + u16 vrfb_height; |
| 22234 | + u8 ctx = vrfb->context; |
| 22235 | + u32 size; |
| 22236 | + u32 control; |
| 22237 | + |
| 22238 | + DBG("omapfb_set_vrfb(%d, %lx, %dx%d, %d)\n", ctx, paddr, |
| 22239 | + width, height, color_mode); |
| 22240 | + |
| 22241 | + /* For YUV2 and UYVY modes VRFB needs to handle pixels a bit |
| 22242 | + * differently. See TRM. */ |
| 22243 | + if (yuv_mode) { |
| 22244 | + bytespp *= 2; |
| 22245 | + width /= 2; |
| 22246 | + } |
| 22247 | + |
| 22248 | + if (bytespp == 4) |
| 22249 | + pixel_size_exp = 2; |
| 22250 | + else if (bytespp == 2) |
| 22251 | + pixel_size_exp = 1; |
| 22252 | + else |
| 22253 | + BUG(); |
| 22254 | + |
| 22255 | + vrfb_width = ALIGN(width * bytespp, VRFB_PAGE_WIDTH) / bytespp; |
| 22256 | + vrfb_height = ALIGN(height, VRFB_PAGE_HEIGHT); |
| 22257 | + |
| 22258 | + DBG("vrfb w %u, h %u bytespp %d\n", vrfb_width, vrfb_height, bytespp); |
| 22259 | + |
| 22260 | + size = vrfb_width << SMS_IMAGEWIDTH_OFFSET; |
| 22261 | + size |= vrfb_height << SMS_IMAGEHEIGHT_OFFSET; |
| 22262 | + |
| 22263 | + control = pixel_size_exp << SMS_PS_OFFSET; |
| 22264 | + control |= VRFB_PAGE_WIDTH_EXP << SMS_PW_OFFSET; |
| 22265 | + control |= VRFB_PAGE_HEIGHT_EXP << SMS_PH_OFFSET; |
| 22266 | + |
| 22267 | + vrfb_hw_context[ctx].physical_ba = paddr; |
| 22268 | + vrfb_hw_context[ctx].size = size; |
| 22269 | + vrfb_hw_context[ctx].control = control; |
| 22270 | + |
| 22271 | + omap2_sms_write_rot_physical_ba(paddr, ctx); |
| 22272 | + omap2_sms_write_rot_size(size, ctx); |
| 22273 | + omap2_sms_write_rot_control(control, ctx); |
| 22274 | + |
| 22275 | + DBG("vrfb offset pixels %d, %d\n", |
| 22276 | + vrfb_width - width, vrfb_height - height); |
| 22277 | + |
| 22278 | + vrfb->xoffset = vrfb_width - width; |
| 22279 | + vrfb->yoffset = vrfb_height - height; |
| 22280 | + vrfb->bytespp = bytespp; |
| 22281 | +} |
| 22282 | +EXPORT_SYMBOL(omap_vrfb_setup); |
| 22283 | + |
| 22284 | +void omap_vrfb_release_ctx(struct vrfb *vrfb) |
| 22285 | +{ |
| 22286 | + int rot; |
| 22287 | + int ctx = vrfb->context; |
| 22288 | + |
| 22289 | + if (ctx == 0xff) |
| 22290 | + return; |
| 22291 | + |
| 22292 | + DBG("release ctx %d\n", ctx); |
| 22293 | + |
| 22294 | + mutex_lock(&ctx_lock); |
| 22295 | + |
| 22296 | + BUG_ON(!(ctx_map & (1 << ctx))); |
| 22297 | + |
| 22298 | + clear_bit(ctx, &ctx_map_active); |
| 22299 | + clear_bit(ctx, &ctx_map); |
| 22300 | + |
| 22301 | + for (rot = 0; rot < 4; ++rot) { |
| 22302 | + if (vrfb->paddr[rot]) { |
| 22303 | + release_mem_region(vrfb->paddr[rot], OMAP_VRFB_SIZE); |
| 22304 | + vrfb->paddr[rot] = 0; |
| 22305 | + } |
| 22306 | + } |
| 22307 | + |
| 22308 | + vrfb->context = 0xff; |
| 22309 | + |
| 22310 | + mutex_unlock(&ctx_lock); |
| 22311 | +} |
| 22312 | +EXPORT_SYMBOL(omap_vrfb_release_ctx); |
| 22313 | + |
| 22314 | +int omap_vrfb_request_ctx(struct vrfb *vrfb) |
| 22315 | +{ |
| 22316 | + int rot; |
| 22317 | + u32 paddr; |
| 22318 | + u8 ctx; |
| 22319 | + int r; |
| 22320 | + |
| 22321 | + DBG("request ctx\n"); |
| 22322 | + |
| 22323 | + mutex_lock(&ctx_lock); |
| 22324 | + |
| 22325 | + for (ctx = 0; ctx < VRFB_NUM_CTXS; ++ctx) |
| 22326 | + if ((ctx_map & (1 << ctx)) == 0) |
| 22327 | + break; |
| 22328 | + |
| 22329 | + if (ctx == VRFB_NUM_CTXS) { |
| 22330 | + pr_err("vrfb: no free contexts\n"); |
| 22331 | + r = -EBUSY; |
| 22332 | + goto out; |
| 22333 | + } |
| 22334 | + |
| 22335 | + DBG("found free ctx %d\n", ctx); |
| 22336 | + |
| 22337 | + set_bit(ctx, &ctx_map); |
| 22338 | + WARN_ON(ctx_map_active & (1 << ctx)); |
| 22339 | + set_bit(ctx, &ctx_map_active); |
| 22340 | + |
| 22341 | + memset(vrfb, 0, sizeof(*vrfb)); |
| 22342 | + |
| 22343 | + vrfb->context = ctx; |
| 22344 | + |
| 22345 | + for (rot = 0; rot < 4; ++rot) { |
| 22346 | + paddr = SMS_ROT_VIRT_BASE(ctx, rot); |
| 22347 | + if (!request_mem_region(paddr, OMAP_VRFB_SIZE, "vrfb")) { |
| 22348 | + pr_err("vrfb: failed to reserve VRFB " |
| 22349 | + "area for ctx %d, rotation %d\n", |
| 22350 | + ctx, rot * 90); |
| 22351 | + omap_vrfb_release_ctx(vrfb); |
| 22352 | + r = -ENOMEM; |
| 22353 | + goto out; |
| 22354 | + } |
| 22355 | + |
| 22356 | + vrfb->paddr[rot] = paddr; |
| 22357 | + |
| 22358 | + DBG("VRFB %d/%d: %lx\n", ctx, rot*90, vrfb->paddr[rot]); |
| 22359 | + } |
| 22360 | + |
| 22361 | + r = 0; |
| 22362 | +out: |
| 22363 | + mutex_unlock(&ctx_lock); |
| 22364 | + return r; |
| 22365 | +} |
| 22366 | +EXPORT_SYMBOL(omap_vrfb_request_ctx); |
| 22367 | + |
| 22368 | +void omap_vrfb_suspend_ctx(struct vrfb *vrfb) |
| 22369 | +{ |
| 22370 | + DBG("suspend ctx %d\n", vrfb->context); |
| 22371 | + mutex_lock(&ctx_lock); |
| 22372 | + |
| 22373 | + BUG_ON(vrfb->context >= VRFB_NUM_CTXS); |
| 22374 | + BUG_ON(!((1 << vrfb->context) & ctx_map_active)); |
| 22375 | + |
| 22376 | + clear_bit(vrfb->context, &ctx_map_active); |
| 22377 | + mutex_unlock(&ctx_lock); |
| 22378 | +} |
| 22379 | +EXPORT_SYMBOL(omap_vrfb_suspend_ctx); |
| 22380 | + |
| 22381 | +void omap_vrfb_resume_ctx(struct vrfb *vrfb) |
| 22382 | +{ |
| 22383 | + DBG("resume ctx %d\n", vrfb->context); |
| 22384 | + mutex_lock(&ctx_lock); |
| 22385 | + |
| 22386 | + BUG_ON(vrfb->context >= VRFB_NUM_CTXS); |
| 22387 | + BUG_ON((1 << vrfb->context) & ctx_map_active); |
| 22388 | + |
| 22389 | + /* |
| 22390 | + * omap_vrfb_restore_context is normally called by the core domain |
| 22391 | + * save / restore logic, but since this VRFB context was suspended |
| 22392 | + * those calls didn't actually restore the context and now we might |
| 22393 | + * have an invalid context. Do an explicit restore here. |
| 22394 | + */ |
| 22395 | + restore_hw_context(vrfb->context); |
| 22396 | + set_bit(vrfb->context, &ctx_map_active); |
| 22397 | + mutex_unlock(&ctx_lock); |
| 22398 | +} |
| 22399 | +EXPORT_SYMBOL(omap_vrfb_resume_ctx); |
| 22400 | + |
| 22401 | --- /dev/null |
| 22402 | +++ b/include/linux/omapfb.h |
| 22403 | @@ -0,0 +1,242 @@ |
| 22404 | +/* |
| 22405 | + * File: include/linux/omapfb.h |
| 22406 | + * |
| 22407 | + * Framebuffer driver for TI OMAP boards |
| 22408 | + * |
| 22409 | + * Copyright (C) 2004 Nokia Corporation |
| 22410 | + * Author: Imre Deak <imre.deak@nokia.com> |
| 22411 | + * |
| 22412 | + * This program is free software; you can redistribute it and/or modify it |
| 22413 | + * under the terms of the GNU General Public License as published by the |
| 22414 | + * Free Software Foundation; either version 2 of the License, or (at your |
| 22415 | + * option) any later version. |
| 22416 | + * |
| 22417 | + * This program is distributed in the hope that it will be useful, but |
| 22418 | + * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 22419 | + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 22420 | + * General Public License for more details. |
| 22421 | + * |
| 22422 | + * You should have received a copy of the GNU General Public License along |
| 22423 | + * with this program; if not, write to the Free Software Foundation, Inc., |
| 22424 | + * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 22425 | + */ |
| 22426 | + |
| 22427 | +#ifndef __LINUX_OMAPFB_H__ |
| 22428 | +#define __LINUX_OMAPFB_H__ |
| 22429 | + |
| 22430 | +#include <linux/fb.h> |
| 22431 | +#include <linux/ioctl.h> |
| 22432 | +#include <linux/types.h> |
| 22433 | + |
| 22434 | +/* IOCTL commands. */ |
| 22435 | + |
| 22436 | +#define OMAP_IOW(num, dtype) _IOW('O', num, dtype) |
| 22437 | +#define OMAP_IOR(num, dtype) _IOR('O', num, dtype) |
| 22438 | +#define OMAP_IOWR(num, dtype) _IOWR('O', num, dtype) |
| 22439 | +#define OMAP_IO(num) _IO('O', num) |
| 22440 | + |
| 22441 | +#define OMAPFB_MIRROR OMAP_IOW(31, int) |
| 22442 | +#define OMAPFB_SYNC_GFX OMAP_IO(37) |
| 22443 | +#define OMAPFB_VSYNC OMAP_IO(38) |
| 22444 | +#define OMAPFB_SET_UPDATE_MODE OMAP_IOW(40, int) |
| 22445 | +#define OMAPFB_GET_CAPS OMAP_IOR(42, struct omapfb_caps) |
| 22446 | +#define OMAPFB_GET_UPDATE_MODE OMAP_IOW(43, int) |
| 22447 | +#define OMAPFB_LCD_TEST OMAP_IOW(45, int) |
| 22448 | +#define OMAPFB_CTRL_TEST OMAP_IOW(46, int) |
| 22449 | +#define OMAPFB_UPDATE_WINDOW_OLD OMAP_IOW(47, struct omapfb_update_window_old) |
| 22450 | +#define OMAPFB_SET_COLOR_KEY OMAP_IOW(50, struct omapfb_color_key) |
| 22451 | +#define OMAPFB_GET_COLOR_KEY OMAP_IOW(51, struct omapfb_color_key) |
| 22452 | +#define OMAPFB_SETUP_PLANE OMAP_IOW(52, struct omapfb_plane_info) |
| 22453 | +#define OMAPFB_QUERY_PLANE OMAP_IOW(53, struct omapfb_plane_info) |
| 22454 | +#define OMAPFB_UPDATE_WINDOW OMAP_IOW(54, struct omapfb_update_window) |
| 22455 | +#define OMAPFB_SETUP_MEM OMAP_IOW(55, struct omapfb_mem_info) |
| 22456 | +#define OMAPFB_QUERY_MEM OMAP_IOW(56, struct omapfb_mem_info) |
| 22457 | +#define OMAPFB_WAITFORVSYNC OMAP_IO(57) |
| 22458 | +#define OMAPFB_MEMORY_READ OMAP_IOR(58, struct omapfb_memory_read) |
| 22459 | +#define OMAPFB_GET_OVERLAY_COLORMODE OMAP_IOR(59, struct omapfb_ovl_colormode) |
| 22460 | +#define OMAPFB_WAITFORGO OMAP_IO(60) |
| 22461 | +#define OMAPFB_GET_VRAM_INFO OMAP_IOR(61, struct omapfb_vram_info) |
| 22462 | + |
| 22463 | +#define OMAPFB_CAPS_GENERIC_MASK 0x00000fff |
| 22464 | +#define OMAPFB_CAPS_LCDC_MASK 0x00fff000 |
| 22465 | +#define OMAPFB_CAPS_PANEL_MASK 0xff000000 |
| 22466 | + |
| 22467 | +#define OMAPFB_CAPS_MANUAL_UPDATE 0x00001000 |
| 22468 | +#define OMAPFB_CAPS_TEARSYNC 0x00002000 |
| 22469 | +#define OMAPFB_CAPS_PLANE_RELOCATE_MEM 0x00004000 |
| 22470 | +#define OMAPFB_CAPS_PLANE_SCALE 0x00008000 |
| 22471 | +#define OMAPFB_CAPS_WINDOW_PIXEL_DOUBLE 0x00010000 |
| 22472 | +#define OMAPFB_CAPS_WINDOW_SCALE 0x00020000 |
| 22473 | +#define OMAPFB_CAPS_WINDOW_OVERLAY 0x00040000 |
| 22474 | +#define OMAPFB_CAPS_WINDOW_ROTATE 0x00080000 |
| 22475 | +#define OMAPFB_CAPS_SET_BACKLIGHT 0x01000000 |
| 22476 | + |
| 22477 | +/* Values from DSP must map to lower 16-bits */ |
| 22478 | +#define OMAPFB_FORMAT_MASK 0x00ff |
| 22479 | +#define OMAPFB_FORMAT_FLAG_DOUBLE 0x0100 |
| 22480 | +#define OMAPFB_FORMAT_FLAG_TEARSYNC 0x0200 |
| 22481 | +#define OMAPFB_FORMAT_FLAG_FORCE_VSYNC 0x0400 |
| 22482 | +#define OMAPFB_FORMAT_FLAG_ENABLE_OVERLAY 0x0800 |
| 22483 | +#define OMAPFB_FORMAT_FLAG_DISABLE_OVERLAY 0x1000 |
| 22484 | + |
| 22485 | +#define OMAPFB_MEMTYPE_SDRAM 0 |
| 22486 | +#define OMAPFB_MEMTYPE_SRAM 1 |
| 22487 | +#define OMAPFB_MEMTYPE_MAX 1 |
| 22488 | + |
| 22489 | +enum omapfb_color_format { |
| 22490 | + OMAPFB_COLOR_RGB565 = 0, |
| 22491 | + OMAPFB_COLOR_YUV422, |
| 22492 | + OMAPFB_COLOR_YUV420, |
| 22493 | + OMAPFB_COLOR_CLUT_8BPP, |
| 22494 | + OMAPFB_COLOR_CLUT_4BPP, |
| 22495 | + OMAPFB_COLOR_CLUT_2BPP, |
| 22496 | + OMAPFB_COLOR_CLUT_1BPP, |
| 22497 | + OMAPFB_COLOR_RGB444, |
| 22498 | + OMAPFB_COLOR_YUY422, |
| 22499 | + |
| 22500 | + OMAPFB_COLOR_ARGB16, |
| 22501 | + OMAPFB_COLOR_RGB24U, /* RGB24, 32-bit container */ |
| 22502 | + OMAPFB_COLOR_RGB24P, /* RGB24, 24-bit container */ |
| 22503 | + OMAPFB_COLOR_ARGB32, |
| 22504 | + OMAPFB_COLOR_RGBA32, |
| 22505 | + OMAPFB_COLOR_RGBX32, |
| 22506 | +}; |
| 22507 | + |
| 22508 | +struct omapfb_update_window { |
| 22509 | + __u32 x, y; |
| 22510 | + __u32 width, height; |
| 22511 | + __u32 format; |
| 22512 | + __u32 out_x, out_y; |
| 22513 | + __u32 out_width, out_height; |
| 22514 | + __u32 reserved[8]; |
| 22515 | +}; |
| 22516 | + |
| 22517 | +struct omapfb_update_window_old { |
| 22518 | + __u32 x, y; |
| 22519 | + __u32 width, height; |
| 22520 | + __u32 format; |
| 22521 | +}; |
| 22522 | + |
| 22523 | +enum omapfb_plane { |
| 22524 | + OMAPFB_PLANE_GFX = 0, |
| 22525 | + OMAPFB_PLANE_VID1, |
| 22526 | + OMAPFB_PLANE_VID2, |
| 22527 | +}; |
| 22528 | + |
| 22529 | +enum omapfb_channel_out { |
| 22530 | + OMAPFB_CHANNEL_OUT_LCD = 0, |
| 22531 | + OMAPFB_CHANNEL_OUT_DIGIT, |
| 22532 | +}; |
| 22533 | + |
| 22534 | +struct omapfb_plane_info { |
| 22535 | + __u32 pos_x; |
| 22536 | + __u32 pos_y; |
| 22537 | + __u8 enabled; |
| 22538 | + __u8 channel_out; |
| 22539 | + __u8 mirror; |
| 22540 | + __u8 reserved1; |
| 22541 | + __u32 out_width; |
| 22542 | + __u32 out_height; |
| 22543 | + __u32 reserved2[12]; |
| 22544 | +}; |
| 22545 | + |
| 22546 | +struct omapfb_mem_info { |
| 22547 | + __u32 size; |
| 22548 | + __u8 type; |
| 22549 | + __u8 reserved[3]; |
| 22550 | +}; |
| 22551 | + |
| 22552 | +struct omapfb_caps { |
| 22553 | + __u32 ctrl; |
| 22554 | + __u32 plane_color; |
| 22555 | + __u32 wnd_color; |
| 22556 | +}; |
| 22557 | + |
| 22558 | +enum omapfb_color_key_type { |
| 22559 | + OMAPFB_COLOR_KEY_DISABLED = 0, |
| 22560 | + OMAPFB_COLOR_KEY_GFX_DST, |
| 22561 | + OMAPFB_COLOR_KEY_VID_SRC, |
| 22562 | +}; |
| 22563 | + |
| 22564 | +struct omapfb_color_key { |
| 22565 | + __u8 channel_out; |
| 22566 | + __u32 background; |
| 22567 | + __u32 trans_key; |
| 22568 | + __u8 key_type; |
| 22569 | +}; |
| 22570 | + |
| 22571 | +enum omapfb_update_mode { |
| 22572 | + OMAPFB_UPDATE_DISABLED = 0, |
| 22573 | + OMAPFB_AUTO_UPDATE, |
| 22574 | + OMAPFB_MANUAL_UPDATE |
| 22575 | +}; |
| 22576 | + |
| 22577 | +struct omapfb_memory_read { |
| 22578 | + __u16 x; |
| 22579 | + __u16 y; |
| 22580 | + __u16 w; |
| 22581 | + __u16 h; |
| 22582 | + size_t buffer_size; |
| 22583 | + void __user *buffer; |
| 22584 | +}; |
| 22585 | + |
| 22586 | +struct omapfb_ovl_colormode { |
| 22587 | + __u8 overlay_idx; |
| 22588 | + __u8 mode_idx; |
| 22589 | + __u32 bits_per_pixel; |
| 22590 | + __u32 nonstd; |
| 22591 | + struct fb_bitfield red; |
| 22592 | + struct fb_bitfield green; |
| 22593 | + struct fb_bitfield blue; |
| 22594 | + struct fb_bitfield transp; |
| 22595 | +}; |
| 22596 | + |
| 22597 | +struct omapfb_vram_info { |
| 22598 | + __u32 total; |
| 22599 | + __u32 free; |
| 22600 | + __u32 largest_free_block; |
| 22601 | + __u32 reserved[5]; |
| 22602 | +}; |
| 22603 | + |
| 22604 | +#ifdef __KERNEL__ |
| 22605 | + |
| 22606 | +#include <mach/board.h> |
| 22607 | + |
| 22608 | +#ifdef CONFIG_ARCH_OMAP1 |
| 22609 | +#define OMAPFB_PLANE_NUM 1 |
| 22610 | +#else |
| 22611 | +#define OMAPFB_PLANE_NUM 3 |
| 22612 | +#endif |
| 22613 | + |
| 22614 | +struct omapfb_mem_region { |
| 22615 | + u32 paddr; |
| 22616 | + void __iomem *vaddr; |
| 22617 | + unsigned long size; |
| 22618 | + u8 type; /* OMAPFB_PLANE_MEM_* */ |
| 22619 | + enum omapfb_color_format format;/* OMAPFB_COLOR_* */ |
| 22620 | + unsigned format_used:1; /* Must be set when format is set. |
| 22621 | + * Needed b/c of the badly chosen 0 |
| 22622 | + * base for OMAPFB_COLOR_* values |
| 22623 | + */ |
| 22624 | + unsigned alloc:1; /* allocated by the driver */ |
| 22625 | + unsigned map:1; /* kernel mapped by the driver */ |
| 22626 | +}; |
| 22627 | + |
| 22628 | +struct omapfb_mem_desc { |
| 22629 | + int region_cnt; |
| 22630 | + struct omapfb_mem_region region[OMAPFB_PLANE_NUM]; |
| 22631 | +}; |
| 22632 | + |
| 22633 | +struct omapfb_platform_data { |
| 22634 | + struct omap_lcd_config lcd; |
| 22635 | + struct omapfb_mem_desc mem_desc; |
| 22636 | + void *ctrl_platform_data; |
| 22637 | +}; |
| 22638 | + |
| 22639 | +/* in arch/arm/plat-omap/fb.c */ |
| 22640 | +extern void omapfb_set_ctrl_platform_data(void *pdata); |
| 22641 | +extern void omapfb_reserve_sdram(void); |
| 22642 | + |
| 22643 | +#endif |
| 22644 | + |
| 22645 | +#endif /* __OMAPFB_H */ |
| 22646 | |