2 * LCD support for iPAQ H5400
4 * Copyright © 2003 Keith Packard
5 * Copyright © 2003, 2005 Phil Blundell
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
12 #include <linux/module.h>
13 #include <linux/init.h>
14 #include <linux/kernel.h>
15 #include <linux/tty.h>
16 #include <linux/sched.h>
17 #include <linux/delay.h>
19 #include <linux/lcd.h>
20 #include <linux/backlight.h>
22 #include <linux/platform_device.h>
23 #include <linux/soc-old.h>
24 #include <linux/err.h>
25 #include "../drivers/soc/mq11xx.h"
27 #include <asm/mach-types.h>
28 #include <asm/hardware.h>
29 #include <asm/arch/ipaq.h>
30 #include <asm/arch/h5400-asic.h>
31 #include <asm/hardware/samcop_base.h>
33 static struct lcd_device
*mqfb_lcd_device
;
34 static struct backlight_device
*mqfb_backlight_device
;
36 /* protects FP regs during RMW sequences */
37 static spinlock_t fp_regs_lock
;
39 #define MQ_BASE 0x04000000
42 * Some H5400 devices have different LCDs installed that require this
43 * other timing. Phil Blundell was blessed with one of these
44 * This is marked in the ENVEE bit (fp[9] & 2), the sharp
45 * LCD version has ENVEE bit 0, the phillips has that bit 1.
47 static struct mediaq11xx_init_data mq1100_init_phillips
= {
50 /* dc00 */ 0x00000001,
51 /* dc01 */ 0x00000003,
52 /* dc02 */ 0x00000001,
53 /* dc03 NOT SET */ 0x0,
54 /* dc04 */ 0x00000004,
55 /* dc05 */ 0x00000003,
59 /* cc00 */ 0x00000000,
60 /* cc01 */ 0x00001010,
61 /* cc02 */ 0x00000a22,
62 /* cc03 */ 0x00000000,
63 /* cc04 */ 0x00000004,
67 /* mm00 */ 0x00000001,
68 /* mm01 */ 0x1b676ca8,
69 /* mm02 */ 0x00000000,
70 /* mm03 */ 0x00001479,
71 /* mm04 */ 0x6bfc2d76,
75 /* gc00 */ 0x051100c8, /* powered down */
76 /* gc01 */ 0x00000000,
77 /* gc02 */ 0x00f8013b,
78 /* gc03 */ 0x013f0150,
79 /* gc04 */ 0x01370132,
80 /* gc05 */ 0x01510150,
81 /* gc06 */ 0x00000000,
82 /* gc07 NOT SET */ 0x0,
83 /* gc08 */ 0x00ef0000,
84 /* gc09 */ 0x013f0000,
85 /* gc0a */ 0x00000000,
86 /* gc0b */ 0x006e0037,
87 /* gc0c */ 0x00000000,
88 /* gc0d */ 0x00066373,
89 /* gc0e */ 0x000001e0,
90 /* gc0f NOT SET */ 0x0,
91 /* gc10 */ 0x02ff07ff,
92 /* gc11 */ 0x000000ff,
93 /* gc12 NOT SET */ 0x0,
94 /* gc13 NOT SET */ 0x0,
95 /* gc14 */ 0x00000000,
96 /* gc15 */ 0x00000000,
97 /* gc16 */ 0x00000000,
98 /* gc17 */ 0x00000000,
99 /* gc18 */ 0x00000000,
100 /* gc19 */ 0x00000000,
101 /* gc1a */ 0x00000000,
105 /* fp00 */ 0x00006120,
106 /* fp01 */ 0x00bcc02c,
107 /* fp02 */ 0xfffcfcfd,
108 /* fp03 */ 0x00000002,
109 /* fp04 */ 0x80bd0001,
110 /* fp05 */ 0xc3000000,
111 /* fp06 */ 0xa0000000,
112 /* fp07 */ 0x00040005,
113 /* fp08 */ 0x00040005,
114 /* fp09 NOT SET */ 0x0,
115 /* fp0a */ 0x00000000,
116 /* fp0b */ 0x00000000,
117 /* fp0c NOT SET */ 0x0,
118 /* fp0d NOT SET */ 0x0,
119 /* fp0e NOT SET */ 0x0,
120 /* fp0f */ 0x00000120,
121 /* fp10 */ 0x2645a216,
122 /* fp11 */ 0x1941d898,
123 /* fp12 */ 0xc2f5b84c,
124 /* fp13 */ 0x411585dd,
125 /* fp14 */ 0xad1d0a85,
126 /* fp15 */ 0xd7928b26,
127 /* fp16 */ 0x4848e722,
128 /* fp17 */ 0x696d0932,
129 /* fp18 */ 0xf2d20075,
130 /* fp19 */ 0xd547235e,
131 /* fp1a */ 0x310304f6,
132 /* fp1b */ 0x3cef4474,
133 /* fp1c */ 0x22a3d351,
134 /* fp1d */ 0x01825700,
135 /* fp1e */ 0xb7d793d1,
136 /* fp1f */ 0x10906408,
153 /* fp30 */ 0x7ba9b45b,
154 /* fp31 */ 0xb56738c3,
155 /* fp32 */ 0xde47a2ba,
156 /* fp33 */ 0x88241cf0,
157 /* fp34 */ 0x35c661b2,
158 /* fp35 */ 0x7ae04b8d,
159 /* fp36 */ 0x7acb010d,
160 /* fp37 */ 0xa216c122,
161 /* fp38 */ 0xd64fb56a,
162 /* fp39 */ 0xe2b4dea9,
163 /* fp3a */ 0x4267c3f7,
164 /* fp3b */ 0x9b75c799,
165 /* fp3c */ 0x01aee179,
166 /* fp3d */ 0x586fd0a8,
167 /* fp3e */ 0x4eeb130e,
168 /* fp3f */ 0x42604a40,
218 /* fp71 */ 0xced6a94e,
223 /* fp76 */ 0x95f9c859,
224 /* fp77 */ 0x30388dc9,
228 /* ge00 NOT SET */ 0x0,
229 /* ge01 NOT SET */ 0x0,
230 /* ge02 NOT SET */ 0x0,
231 /* ge03 NOT SET */ 0x0,
232 /* ge04 NOT SET */ 0x0,
233 /* ge05 NOT SET */ 0x0,
234 /* ge06 NOT SET */ 0x0,
235 /* ge07 NOT SET */ 0x0,
236 /* ge08 NOT SET */ 0x0,
237 /* ge09 NOT SET */ 0x0,
238 /* ge0a */ 0x40000280,
239 /* ge0b */ 0x00000000,
243 static struct mediaq11xx_init_data mq1100_init_sharp
= {
246 /* dc00 */ 0x00000001,
247 /* dc01 */ 0x00000003,
248 /* dc02 */ 0x00000001,
249 /* dc03 NOT SET */ 0x0,
250 /* dc04 */ 0x00000004,
251 /* dc05 */ 0x00000003,
255 /* cc00 */ 0x00000000,
256 /* cc01 */ 0x00001010,
257 /* cc02 */ 0x000002a2,
258 /* cc03 */ 0x00000000,
259 /* cc04 */ 0x00000004,
263 /* mm00 */ 0x00000001,
264 /* mm01 */ 0x1b676ca8,
265 /* mm02 */ 0x00000000,
266 /* mm03 */ 0x00001479,
267 /* mm04 */ 0x6bfc2d76,
271 /* gc00 */ 0x080100c8, /* powered down */
272 /* gc01 */ 0x00000000,
273 /* gc02 */ 0x00f0011a,
274 /* gc03 */ 0x013f015f,
275 /* gc04 */ 0x011100fa,
276 /* gc05 */ 0x015a0158,
277 /* gc06 */ 0x00000000,
278 /* gc07 NOT SET */ 0x0,
279 /* gc08 */ 0x00ef0000,
280 /* gc09 */ 0x013f0000,
281 /* gc0a */ 0x00000000,
282 /* gc0b */ 0x011700f2,
283 /* gc0c */ 0x00000000,
284 /* gc0d */ 0x00066373,
285 /* gc0e */ 0x000001e0,
286 /* gc0f NOT SET */ 0x0,
287 /* gc10 */ 0x02ff07ff,
288 /* gc11 */ 0x000000ff,
289 /* gc12 NOT SET */ 0x0,
290 /* gc13 NOT SET */ 0x0,
291 /* gc14 */ 0x00000000,
292 /* gc15 */ 0x00000000,
293 /* gc16 */ 0x00000000,
294 /* gc17 */ 0x00000000,
295 /* gc18 */ 0x00000000,
296 /* gc19 */ 0x00000000,
297 /* gc1a */ 0x00000000,
301 /* fp00 */ 0x00006120,
302 /* fp01 */ 0x003d5008,
303 /* fp02 */ 0xfffcfcfd,
304 /* fp03 */ 0x00000002,
305 /* fp04 */ 0x80bd0001,
306 /* fp05 */ 0xc9000000,
307 /* fp06 */ 0x80000000,
308 /* fp07 */ 0x00040005,
309 /* fp08 */ 0x00040004,
310 /* fp09 NOT SET */ 0x0,
311 /* fp0a */ 0x00000000,
312 /* fp0b */ 0x00000000,
313 /* fp0c NOT SET */ 0x0,
314 /* fp0d NOT SET */ 0x0,
315 /* fp0e NOT SET */ 0x0,
316 /* fp0f */ 0x00000120,
317 /* fp10 */ 0x2645a216,
318 /* fp11 */ 0x1941d898,
319 /* fp12 */ 0xc2f5b84c,
320 /* fp13 */ 0x411585dd,
321 /* fp14 */ 0xad1d0a85,
322 /* fp15 */ 0xd7928b26,
323 /* fp16 */ 0x4848e722,
324 /* fp17 */ 0x696d0932,
325 /* fp18 */ 0xf2d20075,
326 /* fp19 */ 0xd547235e,
327 /* fp1a */ 0x310304f6,
328 /* fp1b */ 0x3cef4474,
329 /* fp1c */ 0x22a3d351,
330 /* fp1d */ 0x01825700,
331 /* fp1e */ 0xb7d793d1,
332 /* fp1f */ 0x10906408,
349 /* fp30 */ 0x7ba9b45b,
350 /* fp31 */ 0xb56738c3,
351 /* fp32 */ 0xde47a2ba,
352 /* fp33 */ 0x88241cf0,
353 /* fp34 */ 0x35c661b2,
354 /* fp35 */ 0x7ae04b8d,
355 /* fp36 */ 0x7acb010d,
356 /* fp37 */ 0xa216c122,
357 /* fp38 */ 0xd64fb56a,
358 /* fp39 */ 0xe2b4dea9,
359 /* fp3a */ 0x4267c3f7,
360 /* fp3b */ 0x9b75c799,
361 /* fp3c */ 0x01aee179,
362 /* fp3d */ 0x586fd0a8,
363 /* fp3e */ 0x4eeb130e,
364 /* fp3f */ 0x42604a40,
414 /* fp71 */ 0xced6a94e,
419 /* fp76 */ 0x95f9c859,
420 /* fp77 */ 0x30388dc9,
424 /* ge00 NOT SET */ 0x0,
425 /* ge01 NOT SET */ 0x0,
426 /* ge02 NOT SET */ 0x0,
427 /* ge03 NOT SET */ 0x0,
428 /* ge04 NOT SET */ 0x0,
429 /* ge05 NOT SET */ 0x0,
430 /* ge06 NOT SET */ 0x0,
431 /* ge07 NOT SET */ 0x0,
432 /* ge08 NOT SET */ 0x0,
433 /* ge09 NOT SET */ 0x0,
434 /* ge0a */ 0x40000280,
435 /* ge0b */ 0x00000000,
439 static int h5400_lcd_set_power (struct lcd_device
*lm
, int level
)
441 struct mediaq11xx_base
*mq_base
= class_get_devdata (&lm
->class_dev
);
443 mq_base
->set_power (mq_base
, MEDIAQ_11XX_FP_DEVICE_ID
, (level
< 1) ? 1 : 0);
445 samcop_set_gpio_b (&h5400_samcop
.dev
,
446 SAMCOP_GPIO_GPB_LCD_EN
,
447 (level
< 1) ? SAMCOP_GPIO_GPB_LCD_EN
: 0);
451 static int h5400_lcd_get_power (struct lcd_device
*lm
)
453 if (samcop_get_gpio_b (&h5400_samcop
.dev
) & SAMCOP_GPIO_GPB_MQ_POWER_ON
) {
454 if (samcop_get_gpio_b (&h5400_samcop
.dev
) & SAMCOP_GPIO_GPB_LCD_EN
)
464 h5400_lcd_set_contrast (struct lcd_device
*ld
, int contrast
)
466 struct mediaq11xx_base
*mq_base
= class_get_devdata (&ld
->class_dev
);
469 /* Well... this is kind of tricky but here's how it works:
470 * On 24-bit TFT panels the R,G,B channels are output via
471 * the FD0..FD23 MQ1132' outputs. There are separate enable
472 * bits for these pins in FP07R, which we will use.
473 * Basically we just mask out (setting them to '1')
474 * the lowest 1..8 bits of every color component thus
475 * effectively reducing the number of bits for them.
479 contrast
= 7 ^ contrast
;
481 x
= (1 << contrast
) - 1;
483 mq_base
->regs
->FP
.pin_output_data
|= 0x00ffffff;
484 mq_base
->regs
->FP
.pin_output_select_1
=
485 (mq_base
->regs
->FP
.pin_output_select_1
& 0xff000000) |
486 x
| (x
<< 8) | (x
<< 16);
492 h5400_lcd_get_contrast (struct lcd_device
*ld
)
494 struct mediaq11xx_base
*mq_base
= class_get_devdata (&ld
->class_dev
);
497 x
= (mq_base
->regs
->FP
.pin_output_select_1
& 0x7f);
498 for (c
= 7; x
; x
>>= 1, c
--)
504 static struct lcd_properties mq11xx_fb0_lcd
= {
505 .owner
= THIS_MODULE
,
506 .get_power
= h5400_lcd_get_power
,
507 .set_power
= h5400_lcd_set_power
,
510 .get_contrast
= h5400_lcd_get_contrast
,
511 .set_contrast
= h5400_lcd_set_contrast
,
515 /***********************************************************************************
518 ***********************************************************************************/
521 mq1100fb_set_pwm (struct mediaq11xx_base
*base
, unsigned char chan
, unsigned char level
)
523 unsigned long d
, flags
;
524 int shift
= chan
? 24 : 8;
526 spin_lock_irqsave (&fp_regs_lock
, flags
);
527 d
= base
->regs
->FP
.pulse_width_mod_control
;
528 d
&= ~(0xff << shift
);
530 base
->regs
->FP
.pulse_width_mod_control
= d
;
531 spin_unlock_irqrestore (&fp_regs_lock
, flags
);
535 h5400_backlight_set_brightness (struct backlight_device
*bd
, int brightness
)
537 struct mediaq11xx_base
*mq_base
= class_get_devdata (&bd
->class_dev
);
539 if (brightness
> 0x3ff)
542 mq1100fb_set_pwm (mq_base
, 0, (0x3ff - brightness
) >> 2);
548 h5400_backlight_get_brightness (struct backlight_device
*bd
)
554 h5400_backlight_set_power (struct backlight_device
*bl
, int level
)
556 samcop_set_gpio_b (&h5400_samcop
.dev
, SAMCOP_GPIO_GPB_BACKLIGHT_POWER_ON
,
557 (level
< 1) ? SAMCOP_GPIO_GPB_BACKLIGHT_POWER_ON
: 0);
564 h5400_backlight_get_power (struct backlight_device
*bl
)
566 return (samcop_get_gpio_b (&h5400_samcop
.dev
) & SAMCOP_GPIO_GPB_BACKLIGHT_POWER_ON
) ? 0 : 4;
571 h5400_backlight_update_status (struct backlight_device
*bl
)
573 h5400_backlight_set_power(bl
, bl
->props
->power
);
574 h5400_backlight_set_brightness(bl
, bl
->props
->brightness
);
578 static struct backlight_properties mq11xx_fb0_bl
= {
579 .owner
= THIS_MODULE
,
580 .update_status
= h5400_backlight_update_status
,
581 .max_brightness
= 0x3ff,
582 .get_brightness
= h5400_backlight_get_brightness
,
586 h5400_fp_probe (struct device
*dev
)
588 struct mediaq11xx_base
*mq_base
=
589 (struct mediaq11xx_base
*)dev
->platform_data
;
591 spin_lock_init (&fp_regs_lock
);
593 mqfb_backlight_device
= backlight_device_register ("mq11xx_fb0",
594 mq_base
, &mq11xx_fb0_bl
);
595 if (IS_ERR (mqfb_backlight_device
))
596 return PTR_ERR (mqfb_backlight_device
);
597 mqfb_lcd_device
= lcd_device_register ("mq11xx_fb0", mq_base
,
599 if (IS_ERR (mqfb_lcd_device
)) {
600 backlight_device_unregister (mqfb_backlight_device
);
601 return PTR_ERR (mqfb_lcd_device
);
604 /* Power up the backlight and LCD. */
605 h5400_lcd_set_power (mqfb_lcd_device
, 0);
606 h5400_backlight_set_power (mqfb_backlight_device
, 0);
612 h5400_fp_remove (struct device
*dev
)
614 backlight_device_unregister (mqfb_backlight_device
);
615 lcd_device_unregister (mqfb_lcd_device
);
620 //static platform_device_id h5400_fp_device_ids[] = { { MEDIAQ_11XX_FP_DEVICE_ID }, { 0 } };
622 static struct device_driver h5400_fp_device_driver
= {
623 .name
= "mq11xx_lcd",
624 .bus
= &platform_bus_type
,
625 .probe
= h5400_fp_probe
,
626 .remove
= h5400_fp_remove
,
629 static struct resource mq1100_resources
[] = {
630 /* Synchronous memory */
633 .end
= MQ_BASE
+ MQ11xx_FB_SIZE
- 1,
634 .flags
= IORESOURCE_MEM
,
636 /* Non-synchronous memory */
638 .start
= MQ_BASE
+ MQ11xx_FB_SIZE
+ MQ11xx_REG_SIZE
,
639 .end
= MQ_BASE
+ MQ11xx_FB_SIZE
* 2 - 1,
640 .flags
= IORESOURCE_MEM
,
642 /* MediaQ registers */
644 .start
= MQ_BASE
+ MQ11xx_FB_SIZE
,
645 .end
= MQ_BASE
+ MQ11xx_FB_SIZE
+ MQ11xx_REG_SIZE
- 1,
646 .flags
= IORESOURCE_MEM
,
648 /* MediaQ interrupt number -- @@@FIXME */
650 /* The MediaQ IRQ pin is unconnected on the h5xxx.
652 .start
= (unsigned long)-1,
653 .flags
= IORESOURCE_IRQ
,
658 h5400_mq_release (struct device
*dev
)
660 struct platform_device
*pdev
= to_platform_device (dev
);
664 static const struct platform_device h5400_mq1100
= {
667 .num_resources
= ARRAY_SIZE(mq1100_resources
),
668 .resource
= mq1100_resources
, /* XXX FIXME */
670 .release
= h5400_mq_release
674 static struct mediaq11xx_init_data
*
675 h5400_init_lcd_info (void)
678 struct mediaq11xx_regs
*regs
;
680 regs
= (struct mediaq11xx_regs
*)ioremap (MQ_BASE
+ MQ11xx_FB_SIZE
, MQ_BASE
+ MQ11xx_FB_SIZE
+ 0x1fff);
685 * Turn on the config module
687 regs
->DC
.config_1
= MQ_CONFIG_18_OSCILLATOR_INTERNAL
;
688 mdelay (1); /* wait for the oscillator to stabilize */
689 regs
->DC
.config_2
= MQ_CONFIG_CC_MODULE_ENABLE
;
690 mdelay (1); /* probably not needed... */
691 regs
->FP
.input_control
|= MQ_FP_ENVEE
;
692 pin_input_data
= regs
->FP
.pin_input_data
;
695 if (pin_input_data
& MQ_FP_ENVEE
) {
696 printk ("REMOVEME: detected Philips LCD\n");
697 return &mq1100_init_phillips
;
699 printk ("REMOVEME: detected Sharp LCD\n");
700 return &mq1100_init_sharp
;
704 static struct platform_device
*pdev
;
711 if (! machine_is_h5400 ())
714 pdev
= kmalloc (sizeof (*pdev
), GFP_KERNEL
);
718 /* power up the mediaq */
719 samcop_set_gpio_b (&h5400_samcop
.dev
, SAMCOP_GPIO_GPB_MQ_POWER_ON
, SAMCOP_GPIO_GPB_MQ_POWER_ON
);
722 rc
= driver_register (&h5400_fp_device_driver
);
726 *pdev
= h5400_mq1100
;
727 pdev
->dev
.platform_data
= h5400_init_lcd_info ();
728 rc
= platform_device_register (pdev
);
730 driver_unregister (&h5400_fp_device_driver
);
738 driver_unregister (&h5400_fp_device_driver
);
739 platform_device_unregister (pdev
);
741 samcop_set_gpio_b (&h5400_samcop
.dev
, SAMCOP_GPIO_GPB_MQ_POWER_ON
, 0);
744 module_init (h5400_lcd_init
);
745 module_exit (h5400_lcd_exit
);
747 MODULE_AUTHOR ("Keith Packard <keithp@keithp.com>, Phil Blundell <pb@nexus.co.uk>");
748 MODULE_DESCRIPTION ("Framebuffer driver for iPAQ H5400");
749 MODULE_LICENSE ("GPL");
750 MODULE_SUPPORTED_DEVICE ("h5400_lcd");
751 //MODULE_DEVICE_TABLE (soc, h5400_fp_device_ids);