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 | * linux-arm-kernel@lists.arm.linux.org.uk |
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 <nico@fluxnic.net> |
70 | * - Big cleanup for dynamic selection of machine type at run time. |
71 | * |
72 | * 2000/07/19: Jamey Hicks <jamey@crl.dec.com> |
73 | * - Support for Bitsy aka Compaq iPAQ H3600 added. |
74 | * |
75 | * 2000/08/07: Tak-Shing Chan <tchan.rd@idthk.com> |
76 | * Jeff Sutherland <jsutherland@accelent.com> |
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 <imai@vasara.co.jp> |
83 | * - XP860 support added |
84 | * |
85 | * 2000/08/19: Mark Huang <mhuang@livetoy.com> |
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 <J.A.K.Mouw@its.tudelft.nl> |
94 | * - Updated LART stuff. Fixed some minor bugs. |
95 | * |
96 | * 2000/10/30: Murphy Chen <murphy@mail.dialogue.com.tw> |
97 | * - Pangolin support added |
98 | * |
99 | * 2000/10/31: Roman Jordan <jor@hoeft-wessel.de> |
100 | * - Huw Webpanel support added |
101 | * |
102 | * 2000/11/23: Eric Peng <ericpeng@coventive.com> |
103 | * - Freebird add |
104 | * |
105 | * 2001/02/07: Jamey Hicks <jamey.hicks@compaq.com> |
106 | * Cliff Brake <cbrake@accelent.com> |
107 | * - Added PM callback |
108 | * |
109 | * 2001/05/26: <rmk@arm.linux.org.uk> |
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: <rmk@arm.linux.org.uk> |
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: <rmk@arm.linux.org.uk> |
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: <rmk@arm.linux.org.uk> |
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: <rmk@arm.linux.org.uk> |
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: <rmk@arm.linux.org.uk> |
140 | * - Fix colourmap problems. |
141 | * |
142 | * 2001/07/13: <abraham@2d3d.co.za> |
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: <rmk@arm.linux.org.uk> |
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: <rmk@arm.linux.org.uk> |
154 | * - Halve YRES on dual scan LCDs. |
155 | * |
156 | * 2001/08/22: <rmk@arm.linux.org.uk> |
157 | * - Add b/w iPAQ pixclock value. |
158 | * |
159 | * 2001/10/12: <rmk@arm.linux.org.uk> |
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(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(var: &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(chan: red, bf: &fbi->fb.var.red); |
326 | val |= chan_to_field(chan: green, bf: &fbi->fb.var.green); |
327 | val |= chan_to_field(chan: blue, bf: &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, visual: 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(regno: i, red: 0, green: 0, blue: 0, trans: 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(cmap: &fbi->fb.cmap, fb_info: 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; /* skip over the palette */ |
569 | return dma_mmap_wc(dev: fbi->dev, vma, cpu_addr: fbi->map_cpu, dma_addr: fbi->map_dma, |
570 | size: fbi->map_size); |
571 | } |
572 | |
573 | vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); |
574 | |
575 | return vm_iomap_memory(vma, start: info->fix.mmio_start, len: 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 | // .fb_set_cmap = sa1100fb_set_cmap, |
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(clk: fbi->clk) / 100 / 1000; |
598 | |
599 | pcd *= pixclock; |
600 | pcd /= 10000000; |
601 | |
602 | return pcd + 1; /* make up for integer math truncations */ |
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, pixclock: 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 | /* Update shadow copy atomically */ |
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(var: &fbi->fb.var); |
789 | |
790 | /* enable LCD controller clock */ |
791 | clk_prepare_enable(clk: fbi->clk); |
792 | |
793 | /* Sequence from 11.7.10 */ |
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(desc: fbi->shannon_lcden, value: 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(desc: fbi->shannon_lcden, value: 0); |
822 | |
823 | set_current_state(TASK_UNINTERRUPTIBLE); |
824 | add_wait_queue(wq_head: &fbi->ctrlr_wait, wq_entry: &wait); |
825 | |
826 | /* Clear LCD Status Register */ |
827 | writel_relaxed(~0, fbi->base + LCSR); |
828 | |
829 | lccr0 = readl_relaxed(fbi->base + LCCR0); |
830 | lccr0 &= ~LCCR0_LDM; /* Enable LCD Disable Done Interrupt */ |
831 | writel_relaxed(lccr0, fbi->base + LCCR0); |
832 | lccr0 &= ~LCCR0_LEN; /* Disable LCD Controller */ |
833 | writel_relaxed(lccr0, fbi->base + LCCR0); |
834 | |
835 | schedule_timeout(timeout: 20 * HZ / 1000); |
836 | remove_wait_queue(wq_head: &fbi->ctrlr_wait, wq_entry: &wait); |
837 | |
838 | /* disable LCD controller clock */ |
839 | clk_disable_unprepare(clk: 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, on: 0); |
900 | if (old_state != C_DISABLE_CLKCHANGE) |
901 | sa1100fb_disable_controller(fbi); |
902 | __sa1100fb_lcd_power(fbi, on: 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, on: 1); |
949 | sa1100fb_enable_controller(fbi); |
950 | __sa1100fb_backlight_power(fbi, on: 1); |
951 | } |
952 | break; |
953 | } |
954 | mutex_unlock(lock: &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, pixclock: 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(pdev: 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(pdev: 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(dev: fbi->dev, size: fbi->map_size, dma_addr: &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 | /* Fake monspecs to fill in fbinfo structure */ |
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, size: sizeof(struct sa1100fb_info), GFP_KERNEL); |
1071 | if (!fbi) |
1072 | return NULL; |
1073 | |
1074 | fbi->dev = dev; |
1075 | |
1076 | strcpy(p: 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(fmt: "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 | /* Copy the RGB bitfield overrides */ |
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(dev: &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(dev: &pdev->dev); |
1157 | if (!fbi) |
1158 | return -ENOMEM; |
1159 | |
1160 | fbi->base = devm_platform_ioremap_resource(pdev, index: 0); |
1161 | if (IS_ERR(ptr: fbi->base)) |
1162 | return PTR_ERR(ptr: fbi->base); |
1163 | |
1164 | fbi->clk = devm_clk_get(dev: &pdev->dev, NULL); |
1165 | if (IS_ERR(ptr: fbi->clk)) |
1166 | return PTR_ERR(ptr: fbi->clk); |
1167 | |
1168 | ret = devm_request_irq(dev: &pdev->dev, irq, handler: sa1100fb_handle_irq, irqflags: 0, |
1169 | devname: "LCD" , dev_id: fbi); |
1170 | if (ret) { |
1171 | dev_err(&pdev->dev, "request_irq failed: %d\n" , ret); |
1172 | return ret; |
1173 | } |
1174 | |
1175 | fbi->shannon_lcden = gpiod_get_optional(dev: &pdev->dev, con_id: "shannon-lcden" , |
1176 | flags: GPIOD_OUT_LOW); |
1177 | if (IS_ERR(ptr: fbi->shannon_lcden)) |
1178 | return PTR_ERR(ptr: fbi->shannon_lcden); |
1179 | |
1180 | /* Initialize video memory */ |
1181 | ret = sa1100fb_map_video_memory(fbi); |
1182 | if (ret) |
1183 | return ret; |
1184 | |
1185 | /* |
1186 | * This makes sure that our colour bitfield |
1187 | * descriptors are correctly initialised. |
1188 | */ |
1189 | sa1100fb_check_var(var: &fbi->fb.var, info: &fbi->fb); |
1190 | |
1191 | platform_set_drvdata(pdev, data: fbi); |
1192 | |
1193 | ret = register_framebuffer(fb_info: &fbi->fb); |
1194 | if (ret < 0) { |
1195 | dma_free_wc(dev: fbi->dev, size: fbi->map_size, cpu_addr: fbi->map_cpu, |
1196 | dma_addr: fbi->map_dma); |
1197 | return ret; |
1198 | } |
1199 | |
1200 | #ifdef CONFIG_CPU_FREQ |
1201 | fbi->freq_transition.notifier_call = sa1100fb_freq_transition; |
1202 | cpufreq_register_notifier(nb: &fbi->freq_transition, CPUFREQ_TRANSITION_NOTIFIER); |
1203 | #endif |
1204 | |
1205 | /* This driver cannot be unloaded at the moment */ |
1206 | return 0; |
1207 | } |
1208 | |
1209 | static struct platform_driver sa1100fb_driver = { |
1210 | .probe = sa1100fb_probe, |
1211 | .suspend = sa1100fb_suspend, |
1212 | .resume = sa1100fb_resume, |
1213 | .driver = { |
1214 | .name = "sa11x0-fb" , |
1215 | }, |
1216 | }; |
1217 | |
1218 | static int __init sa1100fb_init(void) |
1219 | { |
1220 | if (fb_get_options(name: "sa1100fb" , NULL)) |
1221 | return -ENODEV; |
1222 | |
1223 | return platform_driver_register(&sa1100fb_driver); |
1224 | } |
1225 | |
1226 | module_init(sa1100fb_init); |
1227 | MODULE_DESCRIPTION("StrongARM-1100/1110 framebuffer driver" ); |
1228 | MODULE_LICENSE("GPL" ); |
1229 | |