| 1 | |
| 2 | * linux/drivers/video/sa1100fb.c
|
| 3 | *
|
| 4 | * Copyright (C) 1999 Eric A. Thomas
|
| 5 | * Based on acornfb.c Copyright (C) Russell King.
|
| 6 | *
|
| 7 | * This file is subject to the terms and conditions of the GNU General Public
|
| 8 | * License. See the file COPYING in the main directory of this archive for
|
| 9 | * more details.
|
| 10 | *
|
| 11 | * StrongARM 1100 LCD Controller Frame Buffer Driver
|
| 12 | *
|
| 13 | * Please direct your questions and comments on this driver to the following
|
| 14 | * email address:
|
| 15 | *
|
| 16 | * [email protected]
|
| 17 | *
|
| 18 | * Clean patches should be sent to the ARM Linux Patch System. Please see the
|
| 19 | * following web page for more information:
|
| 20 | *
|
| 21 | * https://www.arm.linux.org.uk/developer/patches/info.shtml
|
| 22 | *
|
| 23 | * Thank you.
|
| 24 | *
|
| 25 | * Known problems:
|
| 26 | * - With the Neponset plugged into an Assabet, LCD powerdown
|
| 27 | * doesn't work (LCD stays powered up). Therefore we shouldn't
|
| 28 | * blank the screen.
|
| 29 | * - We don't limit the CPU clock rate nor the mode selection
|
| 30 | * according to the available SDRAM bandwidth.
|
| 31 | *
|
| 32 | * Other notes:
|
| 33 | * - Linear grayscale palettes and the kernel.
|
| 34 | * Such code does not belong in the kernel. The kernel frame buffer
|
| 35 | * drivers do not expect a linear colourmap, but a colourmap based on
|
| 36 | * the VT100 standard mapping.
|
| 37 | *
|
| 38 | * If your _userspace_ requires a linear colourmap, then the setup of
|
| 39 | * such a colourmap belongs _in userspace_, not in the kernel. Code
|
| 40 | * to set the colourmap correctly from user space has been sent to
|
| 41 | * David Neuer. It's around 8 lines of C code, plus another 4 to
|
| 42 | * detect if we are using grayscale.
|
| 43 | *
|
| 44 | * - The following must never be specified in a panel definition:
|
| 45 | * LCCR0_LtlEnd, LCCR3_PixClkDiv, LCCR3_VrtSnchL, LCCR3_HorSnchL
|
| 46 | *
|
| 47 | * - The following should be specified:
|
| 48 | * either LCCR0_Color or LCCR0_Mono
|
| 49 | * either LCCR0_Sngl or LCCR0_Dual
|
| 50 | * either LCCR0_Act or LCCR0_Pas
|
| 51 | * either LCCR3_OutEnH or LCCD3_OutEnL
|
| 52 | * either LCCR3_PixRsEdg or LCCR3_PixFlEdg
|
| 53 | * either LCCR3_ACBsDiv or LCCR3_ACBsCntOff
|
| 54 | *
|
| 55 | * Code Status:
|
| 56 | * 1999/04/01:
|
| 57 | * - Driver appears to be working for Brutus 320x200x8bpp mode. Other
|
| 58 | * resolutions are working, but only the 8bpp mode is supported.
|
| 59 | * Changes need to be made to the palette encode and decode routines
|
| 60 | * to support 4 and 16 bpp modes.
|
| 61 | * Driver is not designed to be a module. The FrameBuffer is statically
|
| 62 | * allocated since dynamic allocation of a 300k buffer cannot be
|
| 63 | * guaranteed.
|
| 64 | *
|
| 65 | * 1999/06/17:
|
| 66 | * - FrameBuffer memory is now allocated at run-time when the
|
| 67 | * driver is initialized.
|
| 68 | *
|
| 69 | * 2000/04/10: Nicolas Pitre <[email protected]>
|
| 70 | * - Big cleanup for dynamic selection of machine type at run time.
|
| 71 | *
|
| 72 | * 2000/07/19: Jamey Hicks <[email protected]>
|
| 73 | * - Support for Bitsy aka Compaq iPAQ H3600 added.
|
| 74 | *
|
| 75 | * 2000/08/07: Tak-Shing Chan <[email protected]>
|
| 76 | * Jeff Sutherland <[email protected]>
|
| 77 | * - Resolved an issue caused by a change made to the Assabet's PLD
|
| 78 | * earlier this year which broke the framebuffer driver for newer
|
| 79 | * Phase 4 Assabets. Some other parameters were changed to optimize
|
| 80 | * for the Sharp display.
|
| 81 | *
|
| 82 | * 2000/08/09: Kunihiko IMAI <[email protected]>
|
| 83 | * - XP860 support added
|
| 84 | *
|
| 85 | * 2000/08/19: Mark Huang <[email protected]>
|
| 86 | * - Allows standard options to be passed on the kernel command line
|
| 87 | * for most common passive displays.
|
| 88 | *
|
| 89 | * 2000/08/29:
|
| 90 | * - s/save_flags_cli/local_irq_save/
|
| 91 | * - remove unneeded extra save_flags_cli in sa1100fb_enable_lcd_controller
|
| 92 | *
|
| 93 | * 2000/10/10: Erik Mouw <[email protected]>
|
| 94 | * - Updated LART stuff. Fixed some minor bugs.
|
| 95 | *
|
| 96 | * 2000/10/30: Murphy Chen <[email protected]>
|
| 97 | * - Pangolin support added
|
| 98 | *
|
| 99 | * 2000/10/31: Roman Jordan <[email protected]>
|
| 100 | * - Huw Webpanel support added
|
| 101 | *
|
| 102 | * 2000/11/23: Eric Peng <[email protected]>
|
| 103 | * - Freebird add
|
| 104 | *
|
| 105 | * 2001/02/07: Jamey Hicks <[email protected]>
|
| 106 | * Cliff Brake <[email protected]>
|
| 107 | * - Added PM callback
|
| 108 | *
|
| 109 | * 2001/05/26: <[email protected]>
|
| 110 | * - Fix 16bpp so that (a) we use the right colours rather than some
|
| 111 | * totally random colour depending on what was in page 0, and (b)
|
| 112 | * we don't de-reference a NULL pointer.
|
| 113 | * - remove duplicated implementation of consistent_alloc()
|
| 114 | * - convert dma address types to dma_addr_t
|
| 115 | * - remove unused 'montype' stuff
|
| 116 | * - remove redundant zero inits of init_var after the initial
|
| 117 | * memset.
|
| 118 | * - remove allow_modeset (acornfb idea does not belong here)
|
| 119 | *
|
| 120 | * 2001/05/28: <[email protected]>
|
| 121 | * - massive cleanup - move machine dependent data into structures
|
| 122 | * - I've left various #warnings in - if you see one, and know
|
| 123 | * the hardware concerned, please get in contact with me.
|
| 124 | *
|
| 125 | * 2001/05/31: <[email protected]>
|
| 126 | * - Fix LCCR1 HSW value, fix all machine type specifications to
|
| 127 | * keep values in line. (Please check your machine type specs)
|
| 128 | *
|
| 129 | * 2001/06/10: <[email protected]>
|
| 130 | * - Fiddle with the LCD controller from task context only; mainly
|
| 131 | * so that we can run with interrupts on, and sleep.
|
| 132 | * - Convert #warnings into #errors. No pain, no gain. ;)
|
| 133 | *
|
| 134 | * 2001/06/14: <[email protected]>
|
| 135 | * - Make the palette BPS value for 12bpp come out correctly.
|
| 136 | * - Take notice of "greyscale" on any colour depth.
|
| 137 | * - Make truecolor visuals use the RGB channel encoding information.
|
| 138 | *
|
| 139 | * 2001/07/02: <[email protected]>
|
| 140 | * - Fix colourmap problems.
|
| 141 | *
|
| 142 | * 2001/07/13: <[email protected]>
|
| 143 | * - Added support for the ICP LCD-Kit01 on LART. This LCD is
|
| 144 | * manufactured by Prime View, model no V16C6448AB
|
| 145 | *
|
| 146 | * 2001/07/23: <[email protected]>
|
| 147 | * - Hand merge version from handhelds.org CVS tree. See patch
|
| 148 | * notes for 595/1 for more information.
|
| 149 | * - Drop 12bpp (it's 16bpp with different colour register mappings).
|
| 150 | * - This hardware can not do direct colour. Therefore we don't
|
| 151 | * support it.
|
| 152 | *
|
| 153 | * 2001/07/27: <[email protected]>
|
| 154 | * - Halve YRES on dual scan LCDs.
|
| 155 | *
|
| 156 | * 2001/08/22: <[email protected]>
|
| 157 | * - Add b/w iPAQ pixclock value.
|
| 158 | *
|
| 159 | * 2001/10/12: <[email protected]>
|
| 160 | * - Add patch 681/1 and clean up stork definitions.
|
| 161 |
|
| 162 |
|
| 163 | #include <linux/module.h>
|
| 164 | #include <linux/kernel.h>
|
| 165 | #include <linux/sched.h>
|
| 166 | #include <linux/errno.h>
|
| 167 | #include <linux/string.h>
|
| 168 | #include <linux/interrupt.h>
|
| 169 | #include <linux/slab.h>
|
| 170 | #include <linux/mm.h>
|
| 171 | #include <linux/fb.h>
|
| 172 | #include <linux/delay.h>
|
| 173 | #include <linux/init.h>
|
| 174 | #include <linux/ioport.h>
|
| 175 | #include <linux/cpufreq.h>
|
| 176 | #include <linux/gpio/consumer.h>
|
| 177 | #include <linux/platform_device.h>
|
| 178 | #include <linux/dma-mapping.h>
|
| 179 | #include <linux/mutex.h>
|
| 180 | #include <linux/io.h>
|
| 181 | #include <linux/clk.h>
|
| 182 |
|
| 183 | #include <video/sa1100fb.h>
|
| 184 |
|
| 185 | #include <mach/hardware.h>
|
| 186 | #include <asm/mach-types.h>
|
| 187 |
|
| 188 | |
| 189 | * Complain if VAR is out of range.
|
| 190 |
|
| 191 | #define DEBUG_VAR 1
|
| 192 |
|
| 193 | #include "sa1100fb.h"
|
| 194 |
|
| 195 | static const struct sa1100fb_rgb rgb_4 = {
|
| 196 | .red = { .offset = 0, .length = 4, },
|
| 197 | .green = { .offset = 0, .length = 4, },
|
| 198 | .blue = { .offset = 0, .length = 4, },
|
| 199 | .transp = { .offset = 0, .length = 0, },
|
| 200 | };
|
| 201 |
|
| 202 | static const struct sa1100fb_rgb rgb_8 = {
|
| 203 | .red = { .offset = 0, .length = 8, },
|
| 204 | .green = { .offset = 0, .length = 8, },
|
| 205 | .blue = { .offset = 0, .length = 8, },
|
| 206 | .transp = { .offset = 0, .length = 0, },
|
| 207 | };
|
| 208 |
|
| 209 | static const struct sa1100fb_rgb def_rgb_16 = {
|
| 210 | .red = { .offset = 11, .length = 5, },
|
| 211 | .green = { .offset = 5, .length = 6, },
|
| 212 | .blue = { .offset = 0, .length = 5, },
|
| 213 | .transp = { .offset = 0, .length = 0, },
|
| 214 | };
|
| 215 |
|
| 216 |
|
| 217 |
|
| 218 | static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_info *);
|
| 219 | static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state);
|
| 220 |
|
| 221 | static inline void sa1100fb_schedule_work(struct sa1100fb_info *fbi, u_int state)
|
| 222 | {
|
| 223 | unsigned long flags;
|
| 224 |
|
| 225 | local_irq_save(flags);
|
| 226 | |
| 227 | * We need to handle two requests being made at the same time.
|
| 228 | * There are two important cases:
|
| 229 | * 1. When we are changing VT (C_REENABLE) while unblanking (C_ENABLE)
|
| 230 | * We must perform the unblanking, which will do our REENABLE for us.
|
| 231 | * 2. When we are blanking, but immediately unblank before we have
|
| 232 | * blanked. We do the "REENABLE" thing here as well, just to be sure.
|
| 233 |
|
| 234 | if (fbi->task_state == C_ENABLE && state == C_REENABLE)
|
| 235 | state = (u_int) -1;
|
| 236 | if (fbi->task_state == C_DISABLE && state == C_ENABLE)
|
| 237 | state = C_REENABLE;
|
| 238 |
|
| 239 | if (state != (u_int)-1) {
|
| 240 | fbi->task_state = state;
|
| 241 | schedule_work(&fbi->task);
|
| 242 | }
|
| 243 | local_irq_restore(flags);
|
| 244 | }
|
| 245 |
|
| 246 | static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf)
|
| 247 | {
|
| 248 | chan &= 0xffff;
|
| 249 | chan >>= 16 - bf->length;
|
| 250 | return chan << bf->offset;
|
| 251 | }
|
| 252 |
|
| 253 | |
| 254 | * Convert bits-per-pixel to a hardware palette PBS value.
|
| 255 |
|
| 256 | static inline u_int palette_pbs(struct fb_var_screeninfo *var)
|
| 257 | {
|
| 258 | int ret = 0;
|
| 259 | switch (var->bits_per_pixel) {
|
| 260 | case 4: ret = 0 << 12; break;
|
| 261 | case 8: ret = 1 << 12; break;
|
| 262 | case 16: ret = 2 << 12; break;
|
| 263 | }
|
| 264 | return ret;
|
| 265 | }
|
| 266 |
|
| 267 | static int
|
| 268 | sa1100fb_setpalettereg(u_int regno, u_int red, u_int green, u_int blue,
|
| 269 | u_int trans, struct fb_info *info)
|
| 270 | {
|
| 271 | struct sa1100fb_info *fbi =
|
| 272 | container_of(info, struct sa1100fb_info, fb);
|
| 273 | u_int val, ret = 1;
|
| 274 |
|
| 275 | if (regno < fbi->palette_size) {
|
| 276 | val = ((red >> 4) & 0xf00);
|
| 277 | val |= ((green >> 8) & 0x0f0);
|
| 278 | val |= ((blue >> 12) & 0x00f);
|
| 279 |
|
| 280 | if (regno == 0)
|
| 281 | val |= palette_pbs(&fbi->fb.var);
|
| 282 |
|
| 283 | fbi->palette_cpu[regno] = val;
|
| 284 | ret = 0;
|
| 285 | }
|
| 286 | return ret;
|
| 287 | }
|
| 288 |
|
| 289 | static int
|
| 290 | sa1100fb_setcolreg(u_int regno, u_int red, u_int green, u_int blue,
|
| 291 | u_int trans, struct fb_info *info)
|
| 292 | {
|
| 293 | struct sa1100fb_info *fbi =
|
| 294 | container_of(info, struct sa1100fb_info, fb);
|
| 295 | unsigned int val;
|
| 296 | int ret = 1;
|
| 297 |
|
| 298 | |
| 299 | * If inverse mode was selected, invert all the colours
|
| 300 | * rather than the register number. The register number
|
| 301 | * is what you poke into the framebuffer to produce the
|
| 302 | * colour you requested.
|
| 303 |
|
| 304 | if (fbi->inf->cmap_inverse) {
|
| 305 | red = 0xffff - red;
|
| 306 | green = 0xffff - green;
|
| 307 | blue = 0xffff - blue;
|
| 308 | }
|
| 309 |
|
| 310 | |
| 311 | * If greyscale is true, then we convert the RGB value
|
| 312 | * to greyscale no mater what visual we are using.
|
| 313 |
|
| 314 | if (fbi->fb.var.grayscale)
|
| 315 | red = green = blue = (19595 * red + 38470 * green +
|
| 316 | 7471 * blue) >> 16;
|
| 317 |
|
| 318 | switch (fbi->fb.fix.visual) {
|
| 319 | case FB_VISUAL_TRUECOLOR:
|
| 320 | |
| 321 | * 12 or 16-bit True Colour. We encode the RGB value
|
| 322 | * according to the RGB bitfield information.
|
| 323 |
|
| 324 | if (regno < 16) {
|
| 325 | val = chan_to_field(red, &fbi->fb.var.red);
|
| 326 | val |= chan_to_field(green, &fbi->fb.var.green);
|
| 327 | val |= chan_to_field(blue, &fbi->fb.var.blue);
|
| 328 |
|
| 329 | fbi->pseudo_palette[regno] = val;
|
| 330 | ret = 0;
|
| 331 | }
|
| 332 | break;
|
| 333 |
|
| 334 | case FB_VISUAL_STATIC_PSEUDOCOLOR:
|
| 335 | case FB_VISUAL_PSEUDOCOLOR:
|
| 336 | ret = sa1100fb_setpalettereg(regno, red, green, blue, trans, info);
|
| 337 | break;
|
| 338 | }
|
| 339 |
|
| 340 | return ret;
|
| 341 | }
|
| 342 |
|
| 343 | #ifdef CONFIG_CPU_FREQ
|
| 344 | |
| 345 | * sa1100fb_display_dma_period()
|
| 346 | * Calculate the minimum period (in picoseconds) between two DMA
|
| 347 | * requests for the LCD controller. If we hit this, it means we're
|
| 348 | * doing nothing but LCD DMA.
|
| 349 |
|
| 350 | static inline unsigned int sa1100fb_display_dma_period(struct fb_var_screeninfo *var)
|
| 351 | {
|
| 352 | |
| 353 | * Period = pixclock * bits_per_byte * bytes_per_transfer
|
| 354 | * / memory_bits_per_pixel;
|
| 355 |
|
| 356 | return var->pixclock * 8 * 16 / var->bits_per_pixel;
|
| 357 | }
|
| 358 | #endif
|
| 359 |
|
| 360 | |
| 361 | * sa1100fb_check_var():
|
| 362 | * Round up in the following order: bits_per_pixel, xres,
|
| 363 | * yres, xres_virtual, yres_virtual, xoffset, yoffset, grayscale,
|
| 364 | * bitfields, horizontal timing, vertical timing.
|
| 365 |
|
| 366 | static int
|
| 367 | sa1100fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
|
| 368 | {
|
| 369 | struct sa1100fb_info *fbi =
|
| 370 | container_of(info, struct sa1100fb_info, fb);
|
| 371 | int rgbidx;
|
| 372 |
|
| 373 | if (var->xres < MIN_XRES)
|
| 374 | var->xres = MIN_XRES;
|
| 375 | if (var->yres < MIN_YRES)
|
| 376 | var->yres = MIN_YRES;
|
| 377 | if (var->xres > fbi->inf->xres)
|
| 378 | var->xres = fbi->inf->xres;
|
| 379 | if (var->yres > fbi->inf->yres)
|
| 380 | var->yres = fbi->inf->yres;
|
| 381 | var->xres_virtual = max(var->xres_virtual, var->xres);
|
| 382 | var->yres_virtual = max(var->yres_virtual, var->yres);
|
| 383 |
|
| 384 | dev_dbg(fbi->dev, "var->bits_per_pixel=%d\n", var->bits_per_pixel);
|
| 385 | switch (var->bits_per_pixel) {
|
| 386 | case 4:
|
| 387 | rgbidx = RGB_4;
|
| 388 | break;
|
| 389 | case 8:
|
| 390 | rgbidx = RGB_8;
|
| 391 | break;
|
| 392 | case 16:
|
| 393 | rgbidx = RGB_16;
|
| 394 | break;
|
| 395 | default:
|
| 396 | return -EINVAL;
|
| 397 | }
|
| 398 |
|
| 399 | |
| 400 | * Copy the RGB parameters for this display
|
| 401 | * from the machine specific parameters.
|
| 402 |
|
| 403 | var->red = fbi->rgb[rgbidx]->red;
|
| 404 | var->green = fbi->rgb[rgbidx]->green;
|
| 405 | var->blue = fbi->rgb[rgbidx]->blue;
|
| 406 | var->transp = fbi->rgb[rgbidx]->transp;
|
| 407 |
|
| 408 | dev_dbg(fbi->dev, "RGBT length = %d:%d:%d:%d\n",
|
| 409 | var->red.length, var->green.length, var->blue.length,
|
| 410 | var->transp.length);
|
| 411 |
|
| 412 | dev_dbg(fbi->dev, "RGBT offset = %d:%d:%d:%d\n",
|
| 413 | var->red.offset, var->green.offset, var->blue.offset,
|
| 414 | var->transp.offset);
|
| 415 |
|
| 416 | #ifdef CONFIG_CPU_FREQ
|
| 417 | dev_dbg(fbi->dev, "dma period = %d ps, clock = %ld kHz\n",
|
| 418 | sa1100fb_display_dma_period(var),
|
| 419 | clk_get_rate(fbi->clk) / 1000);
|
| 420 | #endif
|
| 421 |
|
| 422 | return 0;
|
| 423 | }
|
| 424 |
|
| 425 | static void sa1100fb_set_visual(struct sa1100fb_info *fbi, u32 visual)
|
| 426 | {
|
| 427 | if (fbi->inf->set_visual)
|
| 428 | fbi->inf->set_visual(visual);
|
| 429 | }
|
| 430 |
|
| 431 | |
| 432 | * sa1100fb_set_par():
|
| 433 | * Set the user defined part of the display for the specified console
|
| 434 |
|
| 435 | static int sa1100fb_set_par(struct fb_info *info)
|
| 436 | {
|
| 437 | struct sa1100fb_info *fbi =
|
| 438 | container_of(info, struct sa1100fb_info, fb);
|
| 439 | struct fb_var_screeninfo *var = &info->var;
|
| 440 | unsigned long palette_mem_size;
|
| 441 |
|
| 442 | dev_dbg(fbi->dev, "set_par\n");
|
| 443 |
|
| 444 | if (var->bits_per_pixel == 16)
|
| 445 | fbi->fb.fix.visual = FB_VISUAL_TRUECOLOR;
|
| 446 | else if (!fbi->inf->cmap_static)
|
| 447 | fbi->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR;
|
| 448 | else {
|
| 449 | |
| 450 | * Some people have weird ideas about wanting static
|
| 451 | * pseudocolor maps. I suspect their user space
|
| 452 | * applications are broken.
|
| 453 |
|
| 454 | fbi->fb.fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR;
|
| 455 | }
|
| 456 |
|
| 457 | fbi->fb.fix.line_length = var->xres_virtual *
|
| 458 | var->bits_per_pixel / 8;
|
| 459 | fbi->palette_size = var->bits_per_pixel == 8 ? 256 : 16;
|
| 460 |
|
| 461 | palette_mem_size = fbi->palette_size * sizeof(u16);
|
| 462 |
|
| 463 | dev_dbg(fbi->dev, "palette_mem_size = 0x%08lx\n", palette_mem_size);
|
| 464 |
|
| 465 | fbi->palette_cpu = (u16 *)(fbi->map_cpu + PAGE_SIZE - palette_mem_size);
|
| 466 | fbi->palette_dma = fbi->map_dma + PAGE_SIZE - palette_mem_size;
|
| 467 |
|
| 468 | |
| 469 | * Set (any) board control register to handle new color depth
|
| 470 |
|
| 471 | sa1100fb_set_visual(fbi, fbi->fb.fix.visual);
|
| 472 | sa1100fb_activate_var(var, fbi);
|
| 473 |
|
| 474 | return 0;
|
| 475 | }
|
| 476 |
|
| 477 | #if 0 |
| 478 | static int
|
| 479 | sa1100fb_set_cmap(struct fb_cmap *cmap, int kspc, int con,
|
| 480 | struct fb_info *info)
|
| 481 | {
|
| 482 | struct sa1100fb_info *fbi = (struct sa1100fb_info *)info;
|
| 483 |
|
| 484 | /*
|
| 485 | * Make sure the user isn't doing something stupid.
|
| 486 | */
|
| 487 | if (!kspc && (fbi->fb.var.bits_per_pixel == 16 || fbi->inf->cmap_static))
|
| 488 | return -EINVAL;
|
| 489 |
|
| 490 | return gen_set_cmap(cmap, kspc, con, info);
|
| 491 | }
|
| 492 | #endif
|
| 493 |
|
| 494 | |
| 495 | * Formal definition of the VESA spec:
|
| 496 | * On
|
| 497 | * This refers to the state of the display when it is in full operation
|
| 498 | * Stand-By
|
| 499 | * This defines an optional operating state of minimal power reduction with
|
| 500 | * the shortest recovery time
|
| 501 | * Suspend
|
| 502 | * This refers to a level of power management in which substantial power
|
| 503 | * reduction is achieved by the display. The display can have a longer
|
| 504 | * recovery time from this state than from the Stand-by state
|
| 505 | * Off
|
| 506 | * This indicates that the display is consuming the lowest level of power
|
| 507 | * and is non-operational. Recovery from this state may optionally require
|
| 508 | * the user to manually power on the monitor
|
| 509 | *
|
| 510 | * Now, the fbdev driver adds an additional state, (blank), where they
|
| 511 | * turn off the video (maybe by colormap tricks), but don't mess with the
|
| 512 | * video itself: think of it semantically between on and Stand-By.
|
| 513 | *
|
| 514 | * So here's what we should do in our fbdev blank routine:
|
| 515 | *
|
| 516 | * VESA_NO_BLANKING (mode 0) Video on, front/back light on
|
| 517 | * VESA_VSYNC_SUSPEND (mode 1) Video on, front/back light off
|
| 518 | * VESA_HSYNC_SUSPEND (mode 2) Video on, front/back light off
|
| 519 | * VESA_POWERDOWN (mode 3) Video off, front/back light off
|
| 520 | *
|
| 521 | * This will match the matrox implementation.
|
| 522 |
|
| 523 | |
| 524 | * sa1100fb_blank():
|
| 525 | * Blank the display by setting all palette values to zero. Note, the
|
| 526 | * 12 and 16 bpp modes don't really use the palette, so this will not
|
| 527 | * blank the display in all modes.
|
| 528 |
|
| 529 | static int sa1100fb_blank(int blank, struct fb_info *info)
|
| 530 | {
|
| 531 | struct sa1100fb_info *fbi =
|
| 532 | container_of(info, struct sa1100fb_info, fb);
|
| 533 | int i;
|
| 534 |
|
| 535 | dev_dbg(fbi->dev, "sa1100fb_blank: blank=%d\n", blank);
|
| 536 |
|
| 537 | switch (blank) {
|
| 538 | case FB_BLANK_POWERDOWN:
|
| 539 | case FB_BLANK_VSYNC_SUSPEND:
|
| 540 | case FB_BLANK_HSYNC_SUSPEND:
|
| 541 | case FB_BLANK_NORMAL:
|
| 542 | if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||
|
| 543 | fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
|
| 544 | for (i = 0; i < fbi->palette_size; i++)
|
| 545 | sa1100fb_setpalettereg(i, 0, 0, 0, 0, info);
|
| 546 | sa1100fb_schedule_work(fbi, C_DISABLE);
|
| 547 | break;
|
| 548 |
|
| 549 | case FB_BLANK_UNBLANK:
|
| 550 | if (fbi->fb.fix.visual == FB_VISUAL_PSEUDOCOLOR ||
|
| 551 | fbi->fb.fix.visual == FB_VISUAL_STATIC_PSEUDOCOLOR)
|
| 552 | fb_set_cmap(&fbi->fb.cmap, info);
|
| 553 | sa1100fb_schedule_work(fbi, C_ENABLE);
|
| 554 | }
|
| 555 | return 0;
|
| 556 | }
|
| 557 |
|
| 558 | static int sa1100fb_mmap(struct fb_info *info,
|
| 559 | struct vm_area_struct *vma)
|
| 560 | {
|
| 561 | struct sa1100fb_info *fbi =
|
| 562 | container_of(info, struct sa1100fb_info, fb);
|
| 563 | unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
|
| 564 |
|
| 565 | vma->vm_page_prot = pgprot_decrypted(vma->vm_page_prot);
|
| 566 |
|
| 567 | if (off < info->fix.smem_len) {
|
| 568 | vma->vm_pgoff += 1;
|
| 569 | return dma_mmap_wc(fbi->dev, vma, fbi->map_cpu, fbi->map_dma,
|
| 570 | fbi->map_size);
|
| 571 | }
|
| 572 |
|
| 573 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
|
| 574 |
|
| 575 | return vm_iomap_memory(vma, info->fix.mmio_start, info->fix.mmio_len);
|
| 576 | }
|
| 577 |
|
| 578 | static const struct fb_ops sa1100fb_ops = {
|
| 579 | .owner = THIS_MODULE,
|
| 580 | __FB_DEFAULT_IOMEM_OPS_RDWR,
|
| 581 | .fb_check_var = sa1100fb_check_var,
|
| 582 | .fb_set_par = sa1100fb_set_par,
|
| 583 | |
| 584 | .fb_setcolreg = sa1100fb_setcolreg,
|
| 585 | .fb_blank = sa1100fb_blank,
|
| 586 | __FB_DEFAULT_IOMEM_OPS_DRAW,
|
| 587 | .fb_mmap = sa1100fb_mmap,
|
| 588 | };
|
| 589 |
|
| 590 | |
| 591 | * Calculate the PCD value from the clock rate (in picoseconds).
|
| 592 | * We take account of the PPCR clock setting.
|
| 593 |
|
| 594 | static inline unsigned int get_pcd(struct sa1100fb_info *fbi,
|
| 595 | unsigned int pixclock)
|
| 596 | {
|
| 597 | unsigned int pcd = clk_get_rate(fbi->clk) / 100 / 1000;
|
| 598 |
|
| 599 | pcd *= pixclock;
|
| 600 | pcd /= 10000000;
|
| 601 |
|
| 602 | return pcd + 1;
|
| 603 | }
|
| 604 |
|
| 605 | |
| 606 | * sa1100fb_activate_var():
|
| 607 | * Configures LCD Controller based on entries in var parameter. Settings are
|
| 608 | * only written to the controller if changes were made.
|
| 609 |
|
| 610 | static int sa1100fb_activate_var(struct fb_var_screeninfo *var, struct sa1100fb_info *fbi)
|
| 611 | {
|
| 612 | struct sa1100fb_lcd_reg new_regs;
|
| 613 | u_int half_screen_size, yres, pcd;
|
| 614 | u_long flags;
|
| 615 |
|
| 616 | dev_dbg(fbi->dev, "Configuring SA1100 LCD\n");
|
| 617 |
|
| 618 | dev_dbg(fbi->dev, "var: xres=%d hslen=%d lm=%d rm=%d\n",
|
| 619 | var->xres, var->hsync_len,
|
| 620 | var->left_margin, var->right_margin);
|
| 621 | dev_dbg(fbi->dev, "var: yres=%d vslen=%d um=%d bm=%d\n",
|
| 622 | var->yres, var->vsync_len,
|
| 623 | var->upper_margin, var->lower_margin);
|
| 624 |
|
| 625 | #if DEBUG_VAR
|
| 626 | if (var->xres < 16 || var->xres > 1024)
|
| 627 | dev_err(fbi->dev, "%s: invalid xres %d\n",
|
| 628 | fbi->fb.fix.id, var->xres);
|
| 629 | if (var->hsync_len < 1 || var->hsync_len > 64)
|
| 630 | dev_err(fbi->dev, "%s: invalid hsync_len %d\n",
|
| 631 | fbi->fb.fix.id, var->hsync_len);
|
| 632 | if (var->left_margin < 1 || var->left_margin > 255)
|
| 633 | dev_err(fbi->dev, "%s: invalid left_margin %d\n",
|
| 634 | fbi->fb.fix.id, var->left_margin);
|
| 635 | if (var->right_margin < 1 || var->right_margin > 255)
|
| 636 | dev_err(fbi->dev, "%s: invalid right_margin %d\n",
|
| 637 | fbi->fb.fix.id, var->right_margin);
|
| 638 | if (var->yres < 1 || var->yres > 1024)
|
| 639 | dev_err(fbi->dev, "%s: invalid yres %d\n",
|
| 640 | fbi->fb.fix.id, var->yres);
|
| 641 | if (var->vsync_len < 1 || var->vsync_len > 64)
|
| 642 | dev_err(fbi->dev, "%s: invalid vsync_len %d\n",
|
| 643 | fbi->fb.fix.id, var->vsync_len);
|
| 644 | if (var->upper_margin < 0 || var->upper_margin > 255)
|
| 645 | dev_err(fbi->dev, "%s: invalid upper_margin %d\n",
|
| 646 | fbi->fb.fix.id, var->upper_margin);
|
| 647 | if (var->lower_margin < 0 || var->lower_margin > 255)
|
| 648 | dev_err(fbi->dev, "%s: invalid lower_margin %d\n",
|
| 649 | fbi->fb.fix.id, var->lower_margin);
|
| 650 | #endif
|
| 651 |
|
| 652 | new_regs.lccr0 = fbi->inf->lccr0 |
|
| 653 | LCCR0_LEN | LCCR0_LDM | LCCR0_BAM |
|
| 654 | LCCR0_ERM | LCCR0_LtlEnd | LCCR0_DMADel(0);
|
| 655 |
|
| 656 | new_regs.lccr1 =
|
| 657 | LCCR1_DisWdth(var->xres) +
|
| 658 | LCCR1_HorSnchWdth(var->hsync_len) +
|
| 659 | LCCR1_BegLnDel(var->left_margin) +
|
| 660 | LCCR1_EndLnDel(var->right_margin);
|
| 661 |
|
| 662 | |
| 663 | * If we have a dual scan LCD, then we need to halve
|
| 664 | * the YRES parameter.
|
| 665 |
|
| 666 | yres = var->yres;
|
| 667 | if (fbi->inf->lccr0 & LCCR0_Dual)
|
| 668 | yres /= 2;
|
| 669 |
|
| 670 | new_regs.lccr2 =
|
| 671 | LCCR2_DisHght(yres) +
|
| 672 | LCCR2_VrtSnchWdth(var->vsync_len) +
|
| 673 | LCCR2_BegFrmDel(var->upper_margin) +
|
| 674 | LCCR2_EndFrmDel(var->lower_margin);
|
| 675 |
|
| 676 | pcd = get_pcd(fbi, var->pixclock);
|
| 677 | new_regs.lccr3 = LCCR3_PixClkDiv(pcd) | fbi->inf->lccr3 |
|
| 678 | (var->sync & FB_SYNC_HOR_HIGH_ACT ? LCCR3_HorSnchH : LCCR3_HorSnchL) |
|
| 679 | (var->sync & FB_SYNC_VERT_HIGH_ACT ? LCCR3_VrtSnchH : LCCR3_VrtSnchL);
|
| 680 |
|
| 681 | dev_dbg(fbi->dev, "nlccr0 = 0x%08lx\n", new_regs.lccr0);
|
| 682 | dev_dbg(fbi->dev, "nlccr1 = 0x%08lx\n", new_regs.lccr1);
|
| 683 | dev_dbg(fbi->dev, "nlccr2 = 0x%08lx\n", new_regs.lccr2);
|
| 684 | dev_dbg(fbi->dev, "nlccr3 = 0x%08lx\n", new_regs.lccr3);
|
| 685 |
|
| 686 | half_screen_size = var->bits_per_pixel;
|
| 687 | half_screen_size = half_screen_size * var->xres * var->yres / 16;
|
| 688 |
|
| 689 |
|
| 690 | local_irq_save(flags);
|
| 691 | fbi->dbar1 = fbi->palette_dma;
|
| 692 | fbi->dbar2 = fbi->screen_dma + half_screen_size;
|
| 693 |
|
| 694 | fbi->reg_lccr0 = new_regs.lccr0;
|
| 695 | fbi->reg_lccr1 = new_regs.lccr1;
|
| 696 | fbi->reg_lccr2 = new_regs.lccr2;
|
| 697 | fbi->reg_lccr3 = new_regs.lccr3;
|
| 698 | local_irq_restore(flags);
|
| 699 |
|
| 700 | |
| 701 | * Only update the registers if the controller is enabled
|
| 702 | * and something has changed.
|
| 703 |
|
| 704 | if (readl_relaxed(fbi->base + LCCR0) != fbi->reg_lccr0 ||
|
| 705 | readl_relaxed(fbi->base + LCCR1) != fbi->reg_lccr1 ||
|
| 706 | readl_relaxed(fbi->base + LCCR2) != fbi->reg_lccr2 ||
|
| 707 | readl_relaxed(fbi->base + LCCR3) != fbi->reg_lccr3 ||
|
| 708 | readl_relaxed(fbi->base + DBAR1) != fbi->dbar1 ||
|
| 709 | readl_relaxed(fbi->base + DBAR2) != fbi->dbar2)
|
| 710 | sa1100fb_schedule_work(fbi, C_REENABLE);
|
| 711 |
|
| 712 | return 0;
|
| 713 | }
|
| 714 |
|
| 715 | |
| 716 | * NOTE! The following functions are purely helpers for set_ctrlr_state.
|
| 717 | * Do not call them directly; set_ctrlr_state does the correct serialisation
|
| 718 | * to ensure that things happen in the right way 100% of time time.
|
| 719 | * -- rmk
|
| 720 |
|
| 721 | static inline void __sa1100fb_backlight_power(struct sa1100fb_info *fbi, int on)
|
| 722 | {
|
| 723 | dev_dbg(fbi->dev, "backlight o%s\n", on ? "n" : "ff");
|
| 724 |
|
| 725 | if (fbi->inf->backlight_power)
|
| 726 | fbi->inf->backlight_power(on);
|
| 727 | }
|
| 728 |
|
| 729 | static inline void __sa1100fb_lcd_power(struct sa1100fb_info *fbi, int on)
|
| 730 | {
|
| 731 | dev_dbg(fbi->dev, "LCD power o%s\n", on ? "n" : "ff");
|
| 732 |
|
| 733 | if (fbi->inf->lcd_power)
|
| 734 | fbi->inf->lcd_power(on);
|
| 735 | }
|
| 736 |
|
| 737 | static void sa1100fb_setup_gpio(struct sa1100fb_info *fbi)
|
| 738 | {
|
| 739 | u_int mask = 0;
|
| 740 |
|
| 741 | |
| 742 | * Enable GPIO<9:2> for LCD use if:
|
| 743 | * 1. Active display, or
|
| 744 | * 2. Color Dual Passive display
|
| 745 | *
|
| 746 | * see table 11.8 on page 11-27 in the SA1100 manual
|
| 747 | * -- Erik.
|
| 748 | *
|
| 749 | * SA1110 spec update nr. 25 says we can and should
|
| 750 | * clear LDD15 to 12 for 4 or 8bpp modes with active
|
| 751 | * panels.
|
| 752 |
|
| 753 | if ((fbi->reg_lccr0 & LCCR0_CMS) == LCCR0_Color &&
|
| 754 | (fbi->reg_lccr0 & (LCCR0_Dual|LCCR0_Act)) != 0) {
|
| 755 | mask = GPIO_LDD11 | GPIO_LDD10 | GPIO_LDD9 | GPIO_LDD8;
|
| 756 |
|
| 757 | if (fbi->fb.var.bits_per_pixel > 8 ||
|
| 758 | (fbi->reg_lccr0 & (LCCR0_Dual|LCCR0_Act)) == LCCR0_Dual)
|
| 759 | mask |= GPIO_LDD15 | GPIO_LDD14 | GPIO_LDD13 | GPIO_LDD12;
|
| 760 |
|
| 761 | }
|
| 762 |
|
| 763 | if (mask) {
|
| 764 | unsigned long flags;
|
| 765 |
|
| 766 | |
| 767 | * SA-1100 requires the GPIO direction register set
|
| 768 | * appropriately for the alternate function. Hence
|
| 769 | * we set it here via bitmask rather than excessive
|
| 770 | * fiddling via the GPIO subsystem - and even then
|
| 771 | * we'll still have to deal with GAFR.
|
| 772 |
|
| 773 | local_irq_save(flags);
|
| 774 | GPDR |= mask;
|
| 775 | GAFR |= mask;
|
| 776 | local_irq_restore(flags);
|
| 777 | }
|
| 778 | }
|
| 779 |
|
| 780 | static void sa1100fb_enable_controller(struct sa1100fb_info *fbi)
|
| 781 | {
|
| 782 | dev_dbg(fbi->dev, "Enabling LCD controller\n");
|
| 783 |
|
| 784 | |
| 785 | * Make sure the mode bits are present in the first palette entry
|
| 786 |
|
| 787 | fbi->palette_cpu[0] &= 0xcfff;
|
| 788 | fbi->palette_cpu[0] |= palette_pbs(&fbi->fb.var);
|
| 789 |
|
| 790 |
|
| 791 | clk_prepare_enable(fbi->clk);
|
| 792 |
|
| 793 |
|
| 794 | writel_relaxed(fbi->reg_lccr3, fbi->base + LCCR3);
|
| 795 | writel_relaxed(fbi->reg_lccr2, fbi->base + LCCR2);
|
| 796 | writel_relaxed(fbi->reg_lccr1, fbi->base + LCCR1);
|
| 797 | writel_relaxed(fbi->reg_lccr0 & ~LCCR0_LEN, fbi->base + LCCR0);
|
| 798 | writel_relaxed(fbi->dbar1, fbi->base + DBAR1);
|
| 799 | writel_relaxed(fbi->dbar2, fbi->base + DBAR2);
|
| 800 | writel_relaxed(fbi->reg_lccr0 | LCCR0_LEN, fbi->base + LCCR0);
|
| 801 |
|
| 802 | if (fbi->shannon_lcden)
|
| 803 | gpiod_set_value(fbi->shannon_lcden, 1);
|
| 804 |
|
| 805 | dev_dbg(fbi->dev, "DBAR1: 0x%08x\n", readl_relaxed(fbi->base + DBAR1));
|
| 806 | dev_dbg(fbi->dev, "DBAR2: 0x%08x\n", readl_relaxed(fbi->base + DBAR2));
|
| 807 | dev_dbg(fbi->dev, "LCCR0: 0x%08x\n", readl_relaxed(fbi->base + LCCR0));
|
| 808 | dev_dbg(fbi->dev, "LCCR1: 0x%08x\n", readl_relaxed(fbi->base + LCCR1));
|
| 809 | dev_dbg(fbi->dev, "LCCR2: 0x%08x\n", readl_relaxed(fbi->base + LCCR2));
|
| 810 | dev_dbg(fbi->dev, "LCCR3: 0x%08x\n", readl_relaxed(fbi->base + LCCR3));
|
| 811 | }
|
| 812 |
|
| 813 | static void sa1100fb_disable_controller(struct sa1100fb_info *fbi)
|
| 814 | {
|
| 815 | DECLARE_WAITQUEUE(wait, current);
|
| 816 | u32 lccr0;
|
| 817 |
|
| 818 | dev_dbg(fbi->dev, "Disabling LCD controller\n");
|
| 819 |
|
| 820 | if (fbi->shannon_lcden)
|
| 821 | gpiod_set_value(fbi->shannon_lcden, 0);
|
| 822 |
|
| 823 | set_current_state(TASK_UNINTERRUPTIBLE);
|
| 824 | add_wait_queue(&fbi->ctrlr_wait, &wait);
|
| 825 |
|
| 826 |
|
| 827 | writel_relaxed(~0, fbi->base + LCSR);
|
| 828 |
|
| 829 | lccr0 = readl_relaxed(fbi->base + LCCR0);
|
| 830 | lccr0 &= ~LCCR0_LDM;
|
| 831 | writel_relaxed(lccr0, fbi->base + LCCR0);
|
| 832 | lccr0 &= ~LCCR0_LEN;
|
| 833 | writel_relaxed(lccr0, fbi->base + LCCR0);
|
| 834 |
|
| 835 | schedule_timeout(20 * HZ / 1000);
|
| 836 | remove_wait_queue(&fbi->ctrlr_wait, &wait);
|
| 837 |
|
| 838 |
|
| 839 | clk_disable_unprepare(fbi->clk);
|
| 840 | }
|
| 841 |
|
| 842 | |
| 843 | * sa1100fb_handle_irq: Handle 'LCD DONE' interrupts.
|
| 844 |
|
| 845 | static irqreturn_t sa1100fb_handle_irq(int irq, void *dev_id)
|
| 846 | {
|
| 847 | struct sa1100fb_info *fbi = dev_id;
|
| 848 | unsigned int lcsr = readl_relaxed(fbi->base + LCSR);
|
| 849 |
|
| 850 | if (lcsr & LCSR_LDD) {
|
| 851 | u32 lccr0 = readl_relaxed(fbi->base + LCCR0) | LCCR0_LDM;
|
| 852 | writel_relaxed(lccr0, fbi->base + LCCR0);
|
| 853 | wake_up(&fbi->ctrlr_wait);
|
| 854 | }
|
| 855 |
|
| 856 | writel_relaxed(lcsr, fbi->base + LCSR);
|
| 857 | return IRQ_HANDLED;
|
| 858 | }
|
| 859 |
|
| 860 | |
| 861 | * This function must be called from task context only, since it will
|
| 862 | * sleep when disabling the LCD controller, or if we get two contending
|
| 863 | * processes trying to alter state.
|
| 864 |
|
| 865 | static void set_ctrlr_state(struct sa1100fb_info *fbi, u_int state)
|
| 866 | {
|
| 867 | u_int old_state;
|
| 868 |
|
| 869 | mutex_lock(&fbi->ctrlr_lock);
|
| 870 |
|
| 871 | old_state = fbi->state;
|
| 872 |
|
| 873 | |
| 874 | * Hack around fbcon initialisation.
|
| 875 |
|
| 876 | if (old_state == C_STARTUP && state == C_REENABLE)
|
| 877 | state = C_ENABLE;
|
| 878 |
|
| 879 | switch (state) {
|
| 880 | case C_DISABLE_CLKCHANGE:
|
| 881 | |
| 882 | * Disable controller for clock change. If the
|
| 883 | * controller is already disabled, then do nothing.
|
| 884 |
|
| 885 | if (old_state != C_DISABLE && old_state != C_DISABLE_PM) {
|
| 886 | fbi->state = state;
|
| 887 | sa1100fb_disable_controller(fbi);
|
| 888 | }
|
| 889 | break;
|
| 890 |
|
| 891 | case C_DISABLE_PM:
|
| 892 | case C_DISABLE:
|
| 893 | |
| 894 | * Disable controller
|
| 895 |
|
| 896 | if (old_state != C_DISABLE) {
|
| 897 | fbi->state = state;
|
| 898 |
|
| 899 | __sa1100fb_backlight_power(fbi, 0);
|
| 900 | if (old_state != C_DISABLE_CLKCHANGE)
|
| 901 | sa1100fb_disable_controller(fbi);
|
| 902 | __sa1100fb_lcd_power(fbi, 0);
|
| 903 | }
|
| 904 | break;
|
| 905 |
|
| 906 | case C_ENABLE_CLKCHANGE:
|
| 907 | |
| 908 | * Enable the controller after clock change. Only
|
| 909 | * do this if we were disabled for the clock change.
|
| 910 |
|
| 911 | if (old_state == C_DISABLE_CLKCHANGE) {
|
| 912 | fbi->state = C_ENABLE;
|
| 913 | sa1100fb_enable_controller(fbi);
|
| 914 | }
|
| 915 | break;
|
| 916 |
|
| 917 | case C_REENABLE:
|
| 918 | |
| 919 | * Re-enable the controller only if it was already
|
| 920 | * enabled. This is so we reprogram the control
|
| 921 | * registers.
|
| 922 |
|
| 923 | if (old_state == C_ENABLE) {
|
| 924 | sa1100fb_disable_controller(fbi);
|
| 925 | sa1100fb_setup_gpio(fbi);
|
| 926 | sa1100fb_enable_controller(fbi);
|
| 927 | }
|
| 928 | break;
|
| 929 |
|
| 930 | case C_ENABLE_PM:
|
| 931 | |
| 932 | * Re-enable the controller after PM. This is not
|
| 933 | * perfect - think about the case where we were doing
|
| 934 | * a clock change, and we suspended half-way through.
|
| 935 |
|
| 936 | if (old_state != C_DISABLE_PM)
|
| 937 | break;
|
| 938 | fallthrough;
|
| 939 |
|
| 940 | case C_ENABLE:
|
| 941 | |
| 942 | * Power up the LCD screen, enable controller, and
|
| 943 | * turn on the backlight.
|
| 944 |
|
| 945 | if (old_state != C_ENABLE) {
|
| 946 | fbi->state = C_ENABLE;
|
| 947 | sa1100fb_setup_gpio(fbi);
|
| 948 | __sa1100fb_lcd_power(fbi, 1);
|
| 949 | sa1100fb_enable_controller(fbi);
|
| 950 | __sa1100fb_backlight_power(fbi, 1);
|
| 951 | }
|
| 952 | break;
|
| 953 | }
|
| 954 | mutex_unlock(&fbi->ctrlr_lock);
|
| 955 | }
|
| 956 |
|
| 957 | |
| 958 | * Our LCD controller task (which is called when we blank or unblank)
|
| 959 | * via keventd.
|
| 960 |
|
| 961 | static void sa1100fb_task(struct work_struct *w)
|
| 962 | {
|
| 963 | struct sa1100fb_info *fbi = container_of(w, struct sa1100fb_info, task);
|
| 964 | u_int state = xchg(&fbi->task_state, -1);
|
| 965 |
|
| 966 | set_ctrlr_state(fbi, state);
|
| 967 | }
|
| 968 |
|
| 969 | #ifdef CONFIG_CPU_FREQ
|
| 970 | |
| 971 | * CPU clock speed change handler. We need to adjust the LCD timing
|
| 972 | * parameters when the CPU clock is adjusted by the power management
|
| 973 | * subsystem.
|
| 974 |
|
| 975 | static int
|
| 976 | sa1100fb_freq_transition(struct notifier_block *nb, unsigned long val,
|
| 977 | void *data)
|
| 978 | {
|
| 979 | struct sa1100fb_info *fbi = TO_INF(nb, freq_transition);
|
| 980 | u_int pcd;
|
| 981 |
|
| 982 | switch (val) {
|
| 983 | case CPUFREQ_PRECHANGE:
|
| 984 | set_ctrlr_state(fbi, C_DISABLE_CLKCHANGE);
|
| 985 | break;
|
| 986 |
|
| 987 | case CPUFREQ_POSTCHANGE:
|
| 988 | pcd = get_pcd(fbi, fbi->fb.var.pixclock);
|
| 989 | fbi->reg_lccr3 = (fbi->reg_lccr3 & ~0xff) | LCCR3_PixClkDiv(pcd);
|
| 990 | set_ctrlr_state(fbi, C_ENABLE_CLKCHANGE);
|
| 991 | break;
|
| 992 | }
|
| 993 | return 0;
|
| 994 | }
|
| 995 | #endif
|
| 996 |
|
| 997 | #ifdef CONFIG_PM
|
| 998 | |
| 999 | * Power management hooks. Note that we won't be called from IRQ context,
|
| 1000 | * unlike the blank functions above, so we may sleep.
|
| 1001 |
|
| 1002 | static int sa1100fb_suspend(struct platform_device *dev, pm_message_t state)
|
| 1003 | {
|
| 1004 | struct sa1100fb_info *fbi = platform_get_drvdata(dev);
|
| 1005 |
|
| 1006 | set_ctrlr_state(fbi, C_DISABLE_PM);
|
| 1007 | return 0;
|
| 1008 | }
|
| 1009 |
|
| 1010 | static int sa1100fb_resume(struct platform_device *dev)
|
| 1011 | {
|
| 1012 | struct sa1100fb_info *fbi = platform_get_drvdata(dev);
|
| 1013 |
|
| 1014 | set_ctrlr_state(fbi, C_ENABLE_PM);
|
| 1015 | return 0;
|
| 1016 | }
|
| 1017 | #else
|
| 1018 | #define sa1100fb_suspend NULL
|
| 1019 | #define sa1100fb_resume NULL
|
| 1020 | #endif
|
| 1021 |
|
| 1022 | |
| 1023 | * sa1100fb_map_video_memory():
|
| 1024 | * Allocates the DRAM memory for the frame buffer. This buffer is
|
| 1025 | * remapped into a non-cached, non-buffered, memory region to
|
| 1026 | * allow palette and pixel writes to occur without flushing the
|
| 1027 | * cache. Once this area is remapped, all virtual memory
|
| 1028 | * access to the video memory should occur at the new region.
|
| 1029 |
|
| 1030 | static int sa1100fb_map_video_memory(struct sa1100fb_info *fbi)
|
| 1031 | {
|
| 1032 | |
| 1033 | * We reserve one page for the palette, plus the size
|
| 1034 | * of the framebuffer.
|
| 1035 |
|
| 1036 | fbi->map_size = PAGE_ALIGN(fbi->fb.fix.smem_len + PAGE_SIZE);
|
| 1037 | fbi->map_cpu = dma_alloc_wc(fbi->dev, fbi->map_size, &fbi->map_dma,
|
| 1038 | GFP_KERNEL);
|
| 1039 |
|
| 1040 | if (fbi->map_cpu) {
|
| 1041 | fbi->fb.screen_base = fbi->map_cpu + PAGE_SIZE;
|
| 1042 | fbi->screen_dma = fbi->map_dma + PAGE_SIZE;
|
| 1043 | |
| 1044 | * FIXME: this is actually the wrong thing to place in
|
| 1045 | * smem_start. But fbdev suffers from the problem that
|
| 1046 | * it needs an API which doesn't exist (in this case,
|
| 1047 | * dma_writecombine_mmap)
|
| 1048 |
|
| 1049 | fbi->fb.fix.smem_start = fbi->screen_dma;
|
| 1050 | }
|
| 1051 |
|
| 1052 | return fbi->map_cpu ? 0 : -ENOMEM;
|
| 1053 | }
|
| 1054 |
|
| 1055 |
|
| 1056 | static const struct fb_monspecs monspecs = {
|
| 1057 | .hfmin = 30000,
|
| 1058 | .hfmax = 70000,
|
| 1059 | .vfmin = 50,
|
| 1060 | .vfmax = 65,
|
| 1061 | };
|
| 1062 |
|
| 1063 |
|
| 1064 | static struct sa1100fb_info *sa1100fb_init_fbinfo(struct device *dev)
|
| 1065 | {
|
| 1066 | struct sa1100fb_mach_info *inf = dev_get_platdata(dev);
|
| 1067 | struct sa1100fb_info *fbi;
|
| 1068 | unsigned i;
|
| 1069 |
|
| 1070 | fbi = devm_kzalloc(dev, sizeof(struct sa1100fb_info), GFP_KERNEL);
|
| 1071 | if (!fbi)
|
| 1072 | return NULL;
|
| 1073 |
|
| 1074 | fbi->dev = dev;
|
| 1075 |
|
| 1076 | strcpy(fbi->fb.fix.id, SA1100_NAME);
|
| 1077 |
|
| 1078 | fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS;
|
| 1079 | fbi->fb.fix.type_aux = 0;
|
| 1080 | fbi->fb.fix.xpanstep = 0;
|
| 1081 | fbi->fb.fix.ypanstep = 0;
|
| 1082 | fbi->fb.fix.ywrapstep = 0;
|
| 1083 | fbi->fb.fix.accel = FB_ACCEL_NONE;
|
| 1084 |
|
| 1085 | fbi->fb.var.nonstd = 0;
|
| 1086 | fbi->fb.var.activate = FB_ACTIVATE_NOW;
|
| 1087 | fbi->fb.var.height = -1;
|
| 1088 | fbi->fb.var.width = -1;
|
| 1089 | fbi->fb.var.accel_flags = 0;
|
| 1090 | fbi->fb.var.vmode = FB_VMODE_NONINTERLACED;
|
| 1091 |
|
| 1092 | fbi->fb.fbops = &sa1100fb_ops;
|
| 1093 | fbi->fb.monspecs = monspecs;
|
| 1094 | fbi->fb.pseudo_palette = fbi->pseudo_palette;
|
| 1095 |
|
| 1096 | fbi->rgb[RGB_4] = &rgb_4;
|
| 1097 | fbi->rgb[RGB_8] = &rgb_8;
|
| 1098 | fbi->rgb[RGB_16] = &def_rgb_16;
|
| 1099 |
|
| 1100 | |
| 1101 | * People just don't seem to get this. We don't support
|
| 1102 | * anything but correct entries now, so panic if someone
|
| 1103 | * does something stupid.
|
| 1104 |
|
| 1105 | if (inf->lccr3 & (LCCR3_VrtSnchL|LCCR3_HorSnchL|0xff) ||
|
| 1106 | inf->pixclock == 0)
|
| 1107 | panic("sa1100fb error: invalid LCCR3 fields set or zero "
|
| 1108 | "pixclock.");
|
| 1109 |
|
| 1110 | fbi->fb.var.xres = inf->xres;
|
| 1111 | fbi->fb.var.xres_virtual = inf->xres;
|
| 1112 | fbi->fb.var.yres = inf->yres;
|
| 1113 | fbi->fb.var.yres_virtual = inf->yres;
|
| 1114 | fbi->fb.var.bits_per_pixel = inf->bpp;
|
| 1115 | fbi->fb.var.pixclock = inf->pixclock;
|
| 1116 | fbi->fb.var.hsync_len = inf->hsync_len;
|
| 1117 | fbi->fb.var.left_margin = inf->left_margin;
|
| 1118 | fbi->fb.var.right_margin = inf->right_margin;
|
| 1119 | fbi->fb.var.vsync_len = inf->vsync_len;
|
| 1120 | fbi->fb.var.upper_margin = inf->upper_margin;
|
| 1121 | fbi->fb.var.lower_margin = inf->lower_margin;
|
| 1122 | fbi->fb.var.sync = inf->sync;
|
| 1123 | fbi->fb.var.grayscale = inf->cmap_greyscale;
|
| 1124 | fbi->state = C_STARTUP;
|
| 1125 | fbi->task_state = (u_char)-1;
|
| 1126 | fbi->fb.fix.smem_len = inf->xres * inf->yres *
|
| 1127 | inf->bpp / 8;
|
| 1128 | fbi->inf = inf;
|
| 1129 |
|
| 1130 |
|
| 1131 | for (i = 0; i < NR_RGB; i++)
|
| 1132 | if (inf->rgb[i])
|
| 1133 | fbi->rgb[i] = inf->rgb[i];
|
| 1134 |
|
| 1135 | init_waitqueue_head(&fbi->ctrlr_wait);
|
| 1136 | INIT_WORK(&fbi->task, sa1100fb_task);
|
| 1137 | mutex_init(&fbi->ctrlr_lock);
|
| 1138 |
|
| 1139 | return fbi;
|
| 1140 | }
|
| 1141 |
|
| 1142 | static int sa1100fb_probe(struct platform_device *pdev)
|
| 1143 | {
|
| 1144 | struct sa1100fb_info *fbi;
|
| 1145 | int ret, irq;
|
| 1146 |
|
| 1147 | if (!dev_get_platdata(&pdev->dev)) {
|
| 1148 | dev_err(&pdev->dev, "no platform LCD data\n");
|
| 1149 | return -EINVAL;
|
| 1150 | }
|
| 1151 |
|
| 1152 | irq = platform_get_irq(pdev, 0);
|
| 1153 | if (irq < 0)
|
| 1154 | return -EINVAL;
|
| 1155 |
|
| 1156 | fbi = sa1100fb_init_fbinfo(&pdev->dev);
|
| 1157 | if (!fbi)
|
| 1158 | return -ENOMEM;
|
| 1159 |
|
| 1160 | fbi->base = devm_platform_ioremap_resource(pdev, 0);
|
| 1161 | if (IS_ERR(fbi->base))
|
| 1162 | return PTR_ERR(fbi->base);
|
| 1163 |
|
| 1164 | fbi->clk = devm_clk_get(&pdev->dev, NULL);
|
| 1165 | if (IS_ERR(fbi->clk))
|
| 1166 | return PTR_ERR(fbi->clk);
|
| 1167 |
|
| 1168 | ret = devm_request_irq(&pdev->dev, irq, sa1100fb_handle_irq, 0,
|
| 1169 | "LCD", fbi);
|
| 1170 | if (ret)
|
| 1171 | return ret;
|
| 1172 |
|
| 1173 | fbi->shannon_lcden = gpiod_get_optional(&pdev->dev, "shannon-lcden",
|
| 1174 | GPIOD_OUT_LOW);
|
| 1175 | if (IS_ERR(fbi->shannon_lcden))
|
| 1176 | return PTR_ERR(fbi->shannon_lcden);
|
| 1177 |
|
| 1178 |
|
| 1179 | ret = sa1100fb_map_video_memory(fbi);
|
| 1180 | if (ret)
|
| 1181 | return ret;
|
| 1182 |
|
| 1183 | |
| 1184 | * This makes sure that our colour bitfield
|
| 1185 | * descriptors are correctly initialised.
|
| 1186 |
|
| 1187 | sa1100fb_check_var(&fbi->fb.var, &fbi->fb);
|
| 1188 |
|
| 1189 | platform_set_drvdata(pdev, fbi);
|
| 1190 |
|
| 1191 | ret = register_framebuffer(&fbi->fb);
|
| 1192 | if (ret < 0) {
|
| 1193 | dma_free_wc(fbi->dev, fbi->map_size, fbi->map_cpu,
|
| 1194 | fbi->map_dma);
|
| 1195 | return ret;
|
| 1196 | }
|
| 1197 |
|
| 1198 | #ifdef CONFIG_CPU_FREQ
|
| 1199 | fbi->freq_transition.notifier_call = sa1100fb_freq_transition;
|
| 1200 | cpufreq_register_notifier(&fbi->freq_transition, CPUFREQ_TRANSITION_NOTIFIER);
|
| 1201 | #endif
|
| 1202 |
|
| 1203 |
|
| 1204 | return 0;
|
| 1205 | }
|
| 1206 |
|
| 1207 | static struct platform_driver sa1100fb_driver = {
|
| 1208 | .probe = sa1100fb_probe,
|
| 1209 | .suspend = sa1100fb_suspend,
|
| 1210 | .resume = sa1100fb_resume,
|
| 1211 | .driver = {
|
| 1212 | .name = "sa11x0-fb",
|
| 1213 | },
|
| 1214 | };
|
| 1215 |
|
| 1216 | static int __init sa1100fb_init(void)
|
| 1217 | {
|
| 1218 | if (fb_get_options("sa1100fb", NULL))
|
| 1219 | return -ENODEV;
|
| 1220 |
|
| 1221 | return platform_driver_register(&sa1100fb_driver);
|
| 1222 | }
|
| 1223 |
|
| 1224 | module_init(sa1100fb_init);
|
| 1225 | MODULE_DESCRIPTION("StrongARM-1100/1110 framebuffer driver");
|
| 1226 | MODULE_LICENSE("GPL");
|