diff options
28 files changed, 4002 insertions, 198 deletions
diff --git a/arch/arm/mach-shmobile/board-ap4evb.c b/arch/arm/mach-shmobile/board-ap4evb.c index d440e5f456ad..a054f0d450d8 100644 --- a/arch/arm/mach-shmobile/board-ap4evb.c +++ b/arch/arm/mach-shmobile/board-ap4evb.c | |||
@@ -501,7 +501,12 @@ static struct platform_device keysc_device = { | |||
501 | static struct resource mipidsi0_resources[] = { | 501 | static struct resource mipidsi0_resources[] = { |
502 | [0] = { | 502 | [0] = { |
503 | .start = 0xffc60000, | 503 | .start = 0xffc60000, |
504 | .end = 0xffc68fff, | 504 | .end = 0xffc63073, |
505 | .flags = IORESOURCE_MEM, | ||
506 | }, | ||
507 | [1] = { | ||
508 | .start = 0xffc68000, | ||
509 | .end = 0xffc680ef, | ||
505 | .flags = IORESOURCE_MEM, | 510 | .flags = IORESOURCE_MEM, |
506 | }, | 511 | }, |
507 | }; | 512 | }; |
@@ -764,10 +769,15 @@ static struct platform_device lcdc1_device = { | |||
764 | }, | 769 | }, |
765 | }; | 770 | }; |
766 | 771 | ||
772 | static long ap4evb_clk_optimize(unsigned long target, unsigned long *best_freq, | ||
773 | unsigned long *parent_freq); | ||
774 | |||
775 | |||
767 | static struct sh_mobile_hdmi_info hdmi_info = { | 776 | static struct sh_mobile_hdmi_info hdmi_info = { |
768 | .lcd_chan = &sh_mobile_lcdc1_info.ch[0], | 777 | .lcd_chan = &sh_mobile_lcdc1_info.ch[0], |
769 | .lcd_dev = &lcdc1_device.dev, | 778 | .lcd_dev = &lcdc1_device.dev, |
770 | .flags = HDMI_SND_SRC_SPDIF, | 779 | .flags = HDMI_SND_SRC_SPDIF, |
780 | .clk_optimize_parent = ap4evb_clk_optimize, | ||
771 | }; | 781 | }; |
772 | 782 | ||
773 | static struct resource hdmi_resources[] = { | 783 | static struct resource hdmi_resources[] = { |
@@ -794,6 +804,25 @@ static struct platform_device hdmi_device = { | |||
794 | }, | 804 | }, |
795 | }; | 805 | }; |
796 | 806 | ||
807 | static long ap4evb_clk_optimize(unsigned long target, unsigned long *best_freq, | ||
808 | unsigned long *parent_freq) | ||
809 | { | ||
810 | struct clk *hdmi_ick = clk_get(&hdmi_device.dev, "ick"); | ||
811 | long error; | ||
812 | |||
813 | if (IS_ERR(hdmi_ick)) { | ||
814 | int ret = PTR_ERR(hdmi_ick); | ||
815 | pr_err("Cannot get HDMI ICK: %d\n", ret); | ||
816 | return ret; | ||
817 | } | ||
818 | |||
819 | error = clk_round_parent(hdmi_ick, target, best_freq, parent_freq, 1, 64); | ||
820 | |||
821 | clk_put(hdmi_ick); | ||
822 | |||
823 | return error; | ||
824 | } | ||
825 | |||
797 | static struct gpio_led ap4evb_leds[] = { | 826 | static struct gpio_led ap4evb_leds[] = { |
798 | { | 827 | { |
799 | .name = "led4", | 828 | .name = "led4", |
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 27c1fb4b1e0d..e231041a5e33 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -186,6 +186,14 @@ config FB_SYS_FOPS | |||
186 | depends on FB | 186 | depends on FB |
187 | default n | 187 | default n |
188 | 188 | ||
189 | config FB_WMT_GE_ROPS | ||
190 | tristate | ||
191 | depends on FB | ||
192 | default n | ||
193 | ---help--- | ||
194 | Include functions for accelerated rectangle filling and area | ||
195 | copying using WonderMedia Graphics Engine operations. | ||
196 | |||
189 | config FB_DEFERRED_IO | 197 | config FB_DEFERRED_IO |
190 | bool | 198 | bool |
191 | depends on FB | 199 | depends on FB |
@@ -635,6 +643,72 @@ config FB_BFIN_LQ035Q1 | |||
635 | To compile this driver as a module, choose M here: the | 643 | To compile this driver as a module, choose M here: the |
636 | module will be called bfin-lq035q1-fb. | 644 | module will be called bfin-lq035q1-fb. |
637 | 645 | ||
646 | config FB_BF537_LQ035 | ||
647 | tristate "SHARP LQ035 TFT LCD (BF537 STAMP)" | ||
648 | depends on FB && (BF534 || BF536 || BF537) && I2C_BLACKFIN_TWI | ||
649 | select FB_CFB_FILLRECT | ||
650 | select FB_CFB_COPYAREA | ||
651 | select FB_CFB_IMAGEBLIT | ||
652 | select BFIN_GPTIMERS | ||
653 | help | ||
654 | This is the framebuffer device for a SHARP LQ035Q7DB03 TFT LCD | ||
655 | attached to a BF537. | ||
656 | |||
657 | To compile this driver as a module, choose M here: the | ||
658 | module will be called bf537-lq035. | ||
659 | |||
660 | config FB_BFIN_7393 | ||
661 | tristate "Blackfin ADV7393 Video encoder" | ||
662 | depends on FB && BLACKFIN | ||
663 | select I2C | ||
664 | select FB_CFB_FILLRECT | ||
665 | select FB_CFB_COPYAREA | ||
666 | select FB_CFB_IMAGEBLIT | ||
667 | help | ||
668 | This is the framebuffer device for a ADV7393 video encoder | ||
669 | attached to a Blackfin on the PPI port. | ||
670 | If your Blackfin board has a ADV7393 select Y. | ||
671 | |||
672 | To compile this driver as a module, choose M here: the | ||
673 | module will be called bfin_adv7393fb. | ||
674 | |||
675 | choice | ||
676 | prompt "Video mode support" | ||
677 | depends on FB_BFIN_7393 | ||
678 | default NTSC | ||
679 | |||
680 | config NTSC | ||
681 | bool 'NTSC 720x480' | ||
682 | |||
683 | config PAL | ||
684 | bool 'PAL 720x576' | ||
685 | |||
686 | config NTSC_640x480 | ||
687 | bool 'NTSC 640x480 (Experimental)' | ||
688 | |||
689 | config PAL_640x480 | ||
690 | bool 'PAL 640x480 (Experimental)' | ||
691 | |||
692 | config NTSC_YCBCR | ||
693 | bool 'NTSC 720x480 YCbCR input' | ||
694 | |||
695 | config PAL_YCBCR | ||
696 | bool 'PAL 720x576 YCbCR input' | ||
697 | |||
698 | endchoice | ||
699 | |||
700 | choice | ||
701 | prompt "Size of ADV7393 frame buffer memory Single/Double Size" | ||
702 | depends on (FB_BFIN_7393) | ||
703 | default ADV7393_1XMEM | ||
704 | |||
705 | config ADV7393_1XMEM | ||
706 | bool 'Single' | ||
707 | |||
708 | config ADV7393_2XMEM | ||
709 | bool 'Double' | ||
710 | endchoice | ||
711 | |||
638 | config FB_STI | 712 | config FB_STI |
639 | tristate "HP STI frame buffer device support" | 713 | tristate "HP STI frame buffer device support" |
640 | depends on FB && PARISC | 714 | depends on FB && PARISC |
@@ -1722,6 +1796,24 @@ config FB_AU1200 | |||
1722 | various panels and CRTs by passing in kernel cmd line option | 1796 | various panels and CRTs by passing in kernel cmd line option |
1723 | au1200fb:panel=<name>. | 1797 | au1200fb:panel=<name>. |
1724 | 1798 | ||
1799 | config FB_VT8500 | ||
1800 | bool "VT8500 LCD Driver" | ||
1801 | depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_VT8500 | ||
1802 | select FB_WMT_GE_ROPS | ||
1803 | select FB_SYS_IMAGEBLIT | ||
1804 | help | ||
1805 | This is the framebuffer driver for VIA VT8500 integrated LCD | ||
1806 | controller. | ||
1807 | |||
1808 | config FB_WM8505 | ||
1809 | bool "WM8505 frame buffer support" | ||
1810 | depends on (FB = y) && ARM && ARCH_VT8500 && VTWM_VERSION_WM8505 | ||
1811 | select FB_WMT_GE_ROPS | ||
1812 | select FB_SYS_IMAGEBLIT | ||
1813 | help | ||
1814 | This is the framebuffer driver for WonderMedia WM8505 | ||
1815 | integrated LCD controller. | ||
1816 | |||
1725 | source "drivers/video/geode/Kconfig" | 1817 | source "drivers/video/geode/Kconfig" |
1726 | 1818 | ||
1727 | config FB_HIT | 1819 | config FB_HIT |
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 485e8ed1318c..bdf626419433 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile | |||
@@ -26,6 +26,7 @@ obj-$(CONFIG_FB_SVGALIB) += svgalib.o | |||
26 | obj-$(CONFIG_FB_MACMODES) += macmodes.o | 26 | obj-$(CONFIG_FB_MACMODES) += macmodes.o |
27 | obj-$(CONFIG_FB_DDC) += fb_ddc.o | 27 | obj-$(CONFIG_FB_DDC) += fb_ddc.o |
28 | obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o | 28 | obj-$(CONFIG_FB_DEFERRED_IO) += fb_defio.o |
29 | obj-$(CONFIG_FB_WMT_GE_ROPS) += wmt_ge_rops.o | ||
29 | 30 | ||
30 | # Hardware specific drivers go first | 31 | # Hardware specific drivers go first |
31 | obj-$(CONFIG_FB_AMIGA) += amifb.o c2p_planar.o | 32 | obj-$(CONFIG_FB_AMIGA) += amifb.o c2p_planar.o |
@@ -104,6 +105,8 @@ obj-$(CONFIG_FB_W100) += w100fb.o | |||
104 | obj-$(CONFIG_FB_TMIO) += tmiofb.o | 105 | obj-$(CONFIG_FB_TMIO) += tmiofb.o |
105 | obj-$(CONFIG_FB_AU1100) += au1100fb.o | 106 | obj-$(CONFIG_FB_AU1100) += au1100fb.o |
106 | obj-$(CONFIG_FB_AU1200) += au1200fb.o | 107 | obj-$(CONFIG_FB_AU1200) += au1200fb.o |
108 | obj-$(CONFIG_FB_VT8500) += vt8500lcdfb.o | ||
109 | obj-$(CONFIG_FB_WM8505) += wm8505fb.o | ||
107 | obj-$(CONFIG_FB_PMAG_AA) += pmag-aa-fb.o | 110 | obj-$(CONFIG_FB_PMAG_AA) += pmag-aa-fb.o |
108 | obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o | 111 | obj-$(CONFIG_FB_PMAG_BA) += pmag-ba-fb.o |
109 | obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o | 112 | obj-$(CONFIG_FB_PMAGB_B) += pmagb-b-fb.o |
@@ -141,9 +144,11 @@ obj-$(CONFIG_FB_VESA) += vesafb.o | |||
141 | obj-$(CONFIG_FB_EFI) += efifb.o | 144 | obj-$(CONFIG_FB_EFI) += efifb.o |
142 | obj-$(CONFIG_FB_VGA16) += vga16fb.o | 145 | obj-$(CONFIG_FB_VGA16) += vga16fb.o |
143 | obj-$(CONFIG_FB_OF) += offb.o | 146 | obj-$(CONFIG_FB_OF) += offb.o |
147 | obj-$(CONFIG_FB_BF537_LQ035) += bf537-lq035.o | ||
144 | obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o | 148 | obj-$(CONFIG_FB_BF54X_LQ043) += bf54x-lq043fb.o |
145 | obj-$(CONFIG_FB_BFIN_LQ035Q1) += bfin-lq035q1-fb.o | 149 | obj-$(CONFIG_FB_BFIN_LQ035Q1) += bfin-lq035q1-fb.o |
146 | obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o | 150 | obj-$(CONFIG_FB_BFIN_T350MCQB) += bfin-t350mcqb-fb.o |
151 | obj-$(CONFIG_FB_BFIN_7393) += bfin_adv7393fb.o | ||
147 | obj-$(CONFIG_FB_MX3) += mx3fb.o | 152 | obj-$(CONFIG_FB_MX3) += mx3fb.o |
148 | obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o | 153 | obj-$(CONFIG_FB_DA8XX) += da8xx-fb.o |
149 | 154 | ||
diff --git a/drivers/video/bf537-lq035.c b/drivers/video/bf537-lq035.c new file mode 100644 index 000000000000..18c507874ff1 --- /dev/null +++ b/drivers/video/bf537-lq035.c | |||
@@ -0,0 +1,914 @@ | |||
1 | /* | ||
2 | * Analog Devices Blackfin(BF537 STAMP) + SHARP TFT LCD. | ||
3 | * http://docs.blackfin.uclinux.org/doku.php?id=hw:cards:tft-lcd | ||
4 | * | ||
5 | * Copyright 2006-2010 Analog Devices Inc. | ||
6 | * Licensed under the GPL-2. | ||
7 | */ | ||
8 | |||
9 | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | ||
10 | |||
11 | #include <linux/module.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/errno.h> | ||
14 | #include <linux/string.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/delay.h> | ||
17 | #include <linux/fb.h> | ||
18 | #include <linux/ioport.h> | ||
19 | #include <linux/init.h> | ||
20 | #include <linux/types.h> | ||
21 | #include <linux/gpio.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/sched.h> | ||
24 | #include <linux/timer.h> | ||
25 | #include <linux/device.h> | ||
26 | #include <linux/backlight.h> | ||
27 | #include <linux/lcd.h> | ||
28 | #include <linux/i2c.h> | ||
29 | #include <linux/spinlock.h> | ||
30 | #include <linux/dma-mapping.h> | ||
31 | #include <linux/slab.h> | ||
32 | #include <linux/platform_device.h> | ||
33 | |||
34 | #include <asm/blackfin.h> | ||
35 | #include <asm/irq.h> | ||
36 | #include <asm/dpmc.h> | ||
37 | #include <asm/dma.h> | ||
38 | #include <asm/portmux.h> | ||
39 | |||
40 | #define NO_BL 1 | ||
41 | |||
42 | #define MAX_BRIGHENESS 95 | ||
43 | #define MIN_BRIGHENESS 5 | ||
44 | #define NBR_PALETTE 256 | ||
45 | |||
46 | static const unsigned short ppi_pins[] = { | ||
47 | P_PPI0_CLK, P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, | ||
48 | P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, | ||
49 | P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11, | ||
50 | P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, 0 | ||
51 | }; | ||
52 | |||
53 | static unsigned char *fb_buffer; /* RGB Buffer */ | ||
54 | static unsigned long *dma_desc_table; | ||
55 | static int t_conf_done, lq035_open_cnt; | ||
56 | static DEFINE_SPINLOCK(bfin_lq035_lock); | ||
57 | |||
58 | static int landscape; | ||
59 | module_param(landscape, int, 0); | ||
60 | MODULE_PARM_DESC(landscape, | ||
61 | "LANDSCAPE use 320x240 instead of Native 240x320 Resolution"); | ||
62 | |||
63 | static int bgr; | ||
64 | module_param(bgr, int, 0); | ||
65 | MODULE_PARM_DESC(bgr, | ||
66 | "BGR use 16-bit BGR-565 instead of RGB-565"); | ||
67 | |||
68 | static int nocursor = 1; | ||
69 | module_param(nocursor, int, 0644); | ||
70 | MODULE_PARM_DESC(nocursor, "cursor enable/disable"); | ||
71 | |||
72 | static unsigned long current_brightness; /* backlight */ | ||
73 | |||
74 | /* AD5280 vcomm */ | ||
75 | static unsigned char vcomm_value = 150; | ||
76 | static struct i2c_client *ad5280_client; | ||
77 | |||
78 | static void set_vcomm(void) | ||
79 | { | ||
80 | int nr; | ||
81 | |||
82 | if (!ad5280_client) | ||
83 | return; | ||
84 | |||
85 | nr = i2c_smbus_write_byte_data(ad5280_client, 0x00, vcomm_value); | ||
86 | if (nr) | ||
87 | pr_err("i2c_smbus_write_byte_data fail: %d\n", nr); | ||
88 | } | ||
89 | |||
90 | static int __devinit ad5280_probe(struct i2c_client *client, | ||
91 | const struct i2c_device_id *id) | ||
92 | { | ||
93 | int ret; | ||
94 | if (!i2c_check_functionality(client->adapter, | ||
95 | I2C_FUNC_SMBUS_BYTE_DATA)) { | ||
96 | dev_err(&client->dev, "SMBUS Byte Data not Supported\n"); | ||
97 | return -EIO; | ||
98 | } | ||
99 | |||
100 | ret = i2c_smbus_write_byte_data(client, 0x00, vcomm_value); | ||
101 | if (ret) { | ||
102 | dev_err(&client->dev, "write fail: %d\n", ret); | ||
103 | return ret; | ||
104 | } | ||
105 | |||
106 | ad5280_client = client; | ||
107 | |||
108 | return 0; | ||
109 | } | ||
110 | |||
111 | static int __devexit ad5280_remove(struct i2c_client *client) | ||
112 | { | ||
113 | ad5280_client = NULL; | ||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | static const struct i2c_device_id ad5280_id[] = { | ||
118 | {"bf537-lq035-ad5280", 0}, | ||
119 | {} | ||
120 | }; | ||
121 | |||
122 | MODULE_DEVICE_TABLE(i2c, ad5280_id); | ||
123 | |||
124 | static struct i2c_driver ad5280_driver = { | ||
125 | .driver = { | ||
126 | .name = "bf537-lq035-ad5280", | ||
127 | }, | ||
128 | .probe = ad5280_probe, | ||
129 | .remove = __devexit_p(ad5280_remove), | ||
130 | .id_table = ad5280_id, | ||
131 | }; | ||
132 | |||
133 | #ifdef CONFIG_PNAV10 | ||
134 | #define MOD GPIO_PH13 | ||
135 | |||
136 | #define bfin_write_TIMER_LP_CONFIG bfin_write_TIMER0_CONFIG | ||
137 | #define bfin_write_TIMER_LP_WIDTH bfin_write_TIMER0_WIDTH | ||
138 | #define bfin_write_TIMER_LP_PERIOD bfin_write_TIMER0_PERIOD | ||
139 | #define bfin_read_TIMER_LP_COUNTER bfin_read_TIMER0_COUNTER | ||
140 | #define TIMDIS_LP TIMDIS0 | ||
141 | #define TIMEN_LP TIMEN0 | ||
142 | |||
143 | #define bfin_write_TIMER_SPS_CONFIG bfin_write_TIMER1_CONFIG | ||
144 | #define bfin_write_TIMER_SPS_WIDTH bfin_write_TIMER1_WIDTH | ||
145 | #define bfin_write_TIMER_SPS_PERIOD bfin_write_TIMER1_PERIOD | ||
146 | #define TIMDIS_SPS TIMDIS1 | ||
147 | #define TIMEN_SPS TIMEN1 | ||
148 | |||
149 | #define bfin_write_TIMER_SP_CONFIG bfin_write_TIMER5_CONFIG | ||
150 | #define bfin_write_TIMER_SP_WIDTH bfin_write_TIMER5_WIDTH | ||
151 | #define bfin_write_TIMER_SP_PERIOD bfin_write_TIMER5_PERIOD | ||
152 | #define TIMDIS_SP TIMDIS5 | ||
153 | #define TIMEN_SP TIMEN5 | ||
154 | |||
155 | #define bfin_write_TIMER_PS_CLS_CONFIG bfin_write_TIMER2_CONFIG | ||
156 | #define bfin_write_TIMER_PS_CLS_WIDTH bfin_write_TIMER2_WIDTH | ||
157 | #define bfin_write_TIMER_PS_CLS_PERIOD bfin_write_TIMER2_PERIOD | ||
158 | #define TIMDIS_PS_CLS TIMDIS2 | ||
159 | #define TIMEN_PS_CLS TIMEN2 | ||
160 | |||
161 | #define bfin_write_TIMER_REV_CONFIG bfin_write_TIMER3_CONFIG | ||
162 | #define bfin_write_TIMER_REV_WIDTH bfin_write_TIMER3_WIDTH | ||
163 | #define bfin_write_TIMER_REV_PERIOD bfin_write_TIMER3_PERIOD | ||
164 | #define TIMDIS_REV TIMDIS3 | ||
165 | #define TIMEN_REV TIMEN3 | ||
166 | #define bfin_read_TIMER_REV_COUNTER bfin_read_TIMER3_COUNTER | ||
167 | |||
168 | #define FREQ_PPI_CLK (5*1024*1024) /* PPI_CLK 5MHz */ | ||
169 | |||
170 | #define TIMERS {P_TMR0, P_TMR1, P_TMR2, P_TMR3, P_TMR5, 0} | ||
171 | |||
172 | #else | ||
173 | |||
174 | #define UD GPIO_PF13 /* Up / Down */ | ||
175 | #define MOD GPIO_PF10 | ||
176 | #define LBR GPIO_PF14 /* Left Right */ | ||
177 | |||
178 | #define bfin_write_TIMER_LP_CONFIG bfin_write_TIMER6_CONFIG | ||
179 | #define bfin_write_TIMER_LP_WIDTH bfin_write_TIMER6_WIDTH | ||
180 | #define bfin_write_TIMER_LP_PERIOD bfin_write_TIMER6_PERIOD | ||
181 | #define bfin_read_TIMER_LP_COUNTER bfin_read_TIMER6_COUNTER | ||
182 | #define TIMDIS_LP TIMDIS6 | ||
183 | #define TIMEN_LP TIMEN6 | ||
184 | |||
185 | #define bfin_write_TIMER_SPS_CONFIG bfin_write_TIMER1_CONFIG | ||
186 | #define bfin_write_TIMER_SPS_WIDTH bfin_write_TIMER1_WIDTH | ||
187 | #define bfin_write_TIMER_SPS_PERIOD bfin_write_TIMER1_PERIOD | ||
188 | #define TIMDIS_SPS TIMDIS1 | ||
189 | #define TIMEN_SPS TIMEN1 | ||
190 | |||
191 | #define bfin_write_TIMER_SP_CONFIG bfin_write_TIMER0_CONFIG | ||
192 | #define bfin_write_TIMER_SP_WIDTH bfin_write_TIMER0_WIDTH | ||
193 | #define bfin_write_TIMER_SP_PERIOD bfin_write_TIMER0_PERIOD | ||
194 | #define TIMDIS_SP TIMDIS0 | ||
195 | #define TIMEN_SP TIMEN0 | ||
196 | |||
197 | #define bfin_write_TIMER_PS_CLS_CONFIG bfin_write_TIMER7_CONFIG | ||
198 | #define bfin_write_TIMER_PS_CLS_WIDTH bfin_write_TIMER7_WIDTH | ||
199 | #define bfin_write_TIMER_PS_CLS_PERIOD bfin_write_TIMER7_PERIOD | ||
200 | #define TIMDIS_PS_CLS TIMDIS7 | ||
201 | #define TIMEN_PS_CLS TIMEN7 | ||
202 | |||
203 | #define bfin_write_TIMER_REV_CONFIG bfin_write_TIMER5_CONFIG | ||
204 | #define bfin_write_TIMER_REV_WIDTH bfin_write_TIMER5_WIDTH | ||
205 | #define bfin_write_TIMER_REV_PERIOD bfin_write_TIMER5_PERIOD | ||
206 | #define TIMDIS_REV TIMDIS5 | ||
207 | #define TIMEN_REV TIMEN5 | ||
208 | #define bfin_read_TIMER_REV_COUNTER bfin_read_TIMER5_COUNTER | ||
209 | |||
210 | #define FREQ_PPI_CLK (6*1000*1000) /* PPI_CLK 6MHz */ | ||
211 | #define TIMERS {P_TMR0, P_TMR1, P_TMR5, P_TMR6, P_TMR7, 0} | ||
212 | |||
213 | #endif | ||
214 | |||
215 | #define LCD_X_RES 240 /* Horizontal Resolution */ | ||
216 | #define LCD_Y_RES 320 /* Vertical Resolution */ | ||
217 | |||
218 | #define LCD_BBP 16 /* Bit Per Pixel */ | ||
219 | |||
220 | /* the LCD and the DMA start counting differently; | ||
221 | * since one starts at 0 and the other starts at 1, | ||
222 | * we have a difference of 1 between START_LINES | ||
223 | * and U_LINES. | ||
224 | */ | ||
225 | #define START_LINES 8 /* lines for field flyback or field blanking signal */ | ||
226 | #define U_LINES 9 /* number of undisplayed blanking lines */ | ||
227 | |||
228 | #define FRAMES_PER_SEC (60) | ||
229 | |||
230 | #define DCLKS_PER_FRAME (FREQ_PPI_CLK/FRAMES_PER_SEC) | ||
231 | #define DCLKS_PER_LINE (DCLKS_PER_FRAME/(LCD_Y_RES+U_LINES)) | ||
232 | |||
233 | #define PPI_CONFIG_VALUE (PORT_DIR|XFR_TYPE|DLEN_16|POLS) | ||
234 | #define PPI_DELAY_VALUE (0) | ||
235 | #define TIMER_CONFIG (PWM_OUT|PERIOD_CNT|TIN_SEL|CLK_SEL) | ||
236 | |||
237 | #define ACTIVE_VIDEO_MEM_OFFSET (LCD_X_RES*START_LINES*(LCD_BBP/8)) | ||
238 | #define ACTIVE_VIDEO_MEM_SIZE (LCD_Y_RES*LCD_X_RES*(LCD_BBP/8)) | ||
239 | #define TOTAL_VIDEO_MEM_SIZE ((LCD_Y_RES+U_LINES)*LCD_X_RES*(LCD_BBP/8)) | ||
240 | #define TOTAL_DMA_DESC_SIZE (2 * sizeof(u32) * (LCD_Y_RES + U_LINES)) | ||
241 | |||
242 | static void start_timers(void) /* CHECK with HW */ | ||
243 | { | ||
244 | unsigned long flags; | ||
245 | |||
246 | local_irq_save(flags); | ||
247 | |||
248 | bfin_write_TIMER_ENABLE(TIMEN_REV); | ||
249 | SSYNC(); | ||
250 | |||
251 | while (bfin_read_TIMER_REV_COUNTER() <= 11) | ||
252 | continue; | ||
253 | bfin_write_TIMER_ENABLE(TIMEN_LP); | ||
254 | SSYNC(); | ||
255 | |||
256 | while (bfin_read_TIMER_LP_COUNTER() < 3) | ||
257 | continue; | ||
258 | bfin_write_TIMER_ENABLE(TIMEN_SP|TIMEN_SPS|TIMEN_PS_CLS); | ||
259 | SSYNC(); | ||
260 | t_conf_done = 1; | ||
261 | local_irq_restore(flags); | ||
262 | } | ||
263 | |||
264 | static void config_timers(void) | ||
265 | { | ||
266 | /* Stop timers */ | ||
267 | bfin_write_TIMER_DISABLE(TIMDIS_SP|TIMDIS_SPS|TIMDIS_REV| | ||
268 | TIMDIS_LP|TIMDIS_PS_CLS); | ||
269 | SSYNC(); | ||
270 | |||
271 | /* LP, timer 6 */ | ||
272 | bfin_write_TIMER_LP_CONFIG(TIMER_CONFIG|PULSE_HI); | ||
273 | bfin_write_TIMER_LP_WIDTH(1); | ||
274 | |||
275 | bfin_write_TIMER_LP_PERIOD(DCLKS_PER_LINE); | ||
276 | SSYNC(); | ||
277 | |||
278 | /* SPS, timer 1 */ | ||
279 | bfin_write_TIMER_SPS_CONFIG(TIMER_CONFIG|PULSE_HI); | ||
280 | bfin_write_TIMER_SPS_WIDTH(DCLKS_PER_LINE*2); | ||
281 | bfin_write_TIMER_SPS_PERIOD((DCLKS_PER_LINE * (LCD_Y_RES+U_LINES))); | ||
282 | SSYNC(); | ||
283 | |||
284 | /* SP, timer 0 */ | ||
285 | bfin_write_TIMER_SP_CONFIG(TIMER_CONFIG|PULSE_HI); | ||
286 | bfin_write_TIMER_SP_WIDTH(1); | ||
287 | bfin_write_TIMER_SP_PERIOD(DCLKS_PER_LINE); | ||
288 | SSYNC(); | ||
289 | |||
290 | /* PS & CLS, timer 7 */ | ||
291 | bfin_write_TIMER_PS_CLS_CONFIG(TIMER_CONFIG); | ||
292 | bfin_write_TIMER_PS_CLS_WIDTH(LCD_X_RES + START_LINES); | ||
293 | bfin_write_TIMER_PS_CLS_PERIOD(DCLKS_PER_LINE); | ||
294 | |||
295 | SSYNC(); | ||
296 | |||
297 | #ifdef NO_BL | ||
298 | /* REV, timer 5 */ | ||
299 | bfin_write_TIMER_REV_CONFIG(TIMER_CONFIG|PULSE_HI); | ||
300 | |||
301 | bfin_write_TIMER_REV_WIDTH(DCLKS_PER_LINE); | ||
302 | bfin_write_TIMER_REV_PERIOD(DCLKS_PER_LINE*2); | ||
303 | |||
304 | SSYNC(); | ||
305 | #endif | ||
306 | } | ||
307 | |||
308 | static void config_ppi(void) | ||
309 | { | ||
310 | bfin_write_PPI_DELAY(PPI_DELAY_VALUE); | ||
311 | bfin_write_PPI_COUNT(LCD_X_RES-1); | ||
312 | /* 0x10 -> PORT_CFG -> 2 or 3 frame syncs */ | ||
313 | bfin_write_PPI_CONTROL((PPI_CONFIG_VALUE|0x10) & (~POLS)); | ||
314 | } | ||
315 | |||
316 | static int config_dma(void) | ||
317 | { | ||
318 | u32 i; | ||
319 | |||
320 | if (landscape) { | ||
321 | |||
322 | for (i = 0; i < U_LINES; ++i) { | ||
323 | /* blanking lines point to first line of fb_buffer */ | ||
324 | dma_desc_table[2*i] = (unsigned long)&dma_desc_table[2*i+2]; | ||
325 | dma_desc_table[2*i+1] = (unsigned long)fb_buffer; | ||
326 | } | ||
327 | |||
328 | for (i = U_LINES; i < U_LINES + LCD_Y_RES; ++i) { | ||
329 | /* visible lines */ | ||
330 | dma_desc_table[2*i] = (unsigned long)&dma_desc_table[2*i+2]; | ||
331 | dma_desc_table[2*i+1] = (unsigned long)fb_buffer + | ||
332 | (LCD_Y_RES+U_LINES-1-i)*2; | ||
333 | } | ||
334 | |||
335 | /* last descriptor points to first */ | ||
336 | dma_desc_table[2*(LCD_Y_RES+U_LINES-1)] = (unsigned long)&dma_desc_table[0]; | ||
337 | |||
338 | set_dma_x_count(CH_PPI, LCD_X_RES); | ||
339 | set_dma_x_modify(CH_PPI, LCD_Y_RES * (LCD_BBP / 8)); | ||
340 | set_dma_y_count(CH_PPI, 0); | ||
341 | set_dma_y_modify(CH_PPI, 0); | ||
342 | set_dma_next_desc_addr(CH_PPI, (void *)dma_desc_table[0]); | ||
343 | set_dma_config(CH_PPI, DMAFLOW_LARGE | NDSIZE_4 | WDSIZE_16); | ||
344 | |||
345 | } else { | ||
346 | |||
347 | set_dma_config(CH_PPI, set_bfin_dma_config(DIR_READ, | ||
348 | DMA_FLOW_AUTO, | ||
349 | INTR_DISABLE, | ||
350 | DIMENSION_2D, | ||
351 | DATA_SIZE_16, | ||
352 | DMA_NOSYNC_KEEP_DMA_BUF)); | ||
353 | set_dma_x_count(CH_PPI, LCD_X_RES); | ||
354 | set_dma_x_modify(CH_PPI, LCD_BBP / 8); | ||
355 | set_dma_y_count(CH_PPI, LCD_Y_RES+U_LINES); | ||
356 | set_dma_y_modify(CH_PPI, LCD_BBP / 8); | ||
357 | set_dma_start_addr(CH_PPI, (unsigned long) fb_buffer); | ||
358 | } | ||
359 | |||
360 | return 0; | ||
361 | } | ||
362 | |||
363 | static int __devinit request_ports(void) | ||
364 | { | ||
365 | u16 tmr_req[] = TIMERS; | ||
366 | |||
367 | /* | ||
368 | UD: PF13 | ||
369 | MOD: PF10 | ||
370 | LBR: PF14 | ||
371 | PPI_CLK: PF15 | ||
372 | */ | ||
373 | |||
374 | if (peripheral_request_list(ppi_pins, KBUILD_MODNAME)) { | ||
375 | pr_err("requesting PPI peripheral failed\n"); | ||
376 | return -EBUSY; | ||
377 | } | ||
378 | |||
379 | if (peripheral_request_list(tmr_req, KBUILD_MODNAME)) { | ||
380 | peripheral_free_list(ppi_pins); | ||
381 | pr_err("requesting timer peripheral failed\n"); | ||
382 | return -EBUSY; | ||
383 | } | ||
384 | |||
385 | #if (defined(UD) && defined(LBR)) | ||
386 | if (gpio_request(UD, KBUILD_MODNAME)) { | ||
387 | pr_err("requesting GPIO %d failed\n", UD); | ||
388 | return -EBUSY; | ||
389 | } | ||
390 | |||
391 | if (gpio_request(LBR, KBUILD_MODNAME)) { | ||
392 | pr_err("requesting GPIO %d failed\n", LBR); | ||
393 | gpio_free(UD); | ||
394 | return -EBUSY; | ||
395 | } | ||
396 | |||
397 | gpio_direction_output(UD, 0); | ||
398 | gpio_direction_output(LBR, 1); | ||
399 | |||
400 | #endif | ||
401 | |||
402 | if (gpio_request(MOD, KBUILD_MODNAME)) { | ||
403 | pr_err("requesting GPIO %d failed\n", MOD); | ||
404 | #if (defined(UD) && defined(LBR)) | ||
405 | gpio_free(LBR); | ||
406 | gpio_free(UD); | ||
407 | #endif | ||
408 | return -EBUSY; | ||
409 | } | ||
410 | |||
411 | gpio_direction_output(MOD, 1); | ||
412 | |||
413 | SSYNC(); | ||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | static void free_ports(void) | ||
418 | { | ||
419 | u16 tmr_req[] = TIMERS; | ||
420 | |||
421 | peripheral_free_list(ppi_pins); | ||
422 | peripheral_free_list(tmr_req); | ||
423 | |||
424 | #if defined(UD) && defined(LBR) | ||
425 | gpio_free(LBR); | ||
426 | gpio_free(UD); | ||
427 | #endif | ||
428 | gpio_free(MOD); | ||
429 | } | ||
430 | |||
431 | static struct fb_info bfin_lq035_fb; | ||
432 | |||
433 | static struct fb_var_screeninfo bfin_lq035_fb_defined = { | ||
434 | .bits_per_pixel = LCD_BBP, | ||
435 | .activate = FB_ACTIVATE_TEST, | ||
436 | .xres = LCD_X_RES, /*default portrait mode RGB*/ | ||
437 | .yres = LCD_Y_RES, | ||
438 | .xres_virtual = LCD_X_RES, | ||
439 | .yres_virtual = LCD_Y_RES, | ||
440 | .height = -1, | ||
441 | .width = -1, | ||
442 | .left_margin = 0, | ||
443 | .right_margin = 0, | ||
444 | .upper_margin = 0, | ||
445 | .lower_margin = 0, | ||
446 | .red = {11, 5, 0}, | ||
447 | .green = {5, 6, 0}, | ||
448 | .blue = {0, 5, 0}, | ||
449 | .transp = {0, 0, 0}, | ||
450 | }; | ||
451 | |||
452 | static struct fb_fix_screeninfo bfin_lq035_fb_fix __devinitdata = { | ||
453 | .id = KBUILD_MODNAME, | ||
454 | .smem_len = ACTIVE_VIDEO_MEM_SIZE, | ||
455 | .type = FB_TYPE_PACKED_PIXELS, | ||
456 | .visual = FB_VISUAL_TRUECOLOR, | ||
457 | .xpanstep = 0, | ||
458 | .ypanstep = 0, | ||
459 | .line_length = LCD_X_RES*(LCD_BBP/8), | ||
460 | .accel = FB_ACCEL_NONE, | ||
461 | }; | ||
462 | |||
463 | |||
464 | static int bfin_lq035_fb_open(struct fb_info *info, int user) | ||
465 | { | ||
466 | unsigned long flags; | ||
467 | |||
468 | spin_lock_irqsave(&bfin_lq035_lock, flags); | ||
469 | lq035_open_cnt++; | ||
470 | spin_unlock_irqrestore(&bfin_lq035_lock, flags); | ||
471 | |||
472 | if (lq035_open_cnt <= 1) { | ||
473 | bfin_write_PPI_CONTROL(0); | ||
474 | SSYNC(); | ||
475 | |||
476 | set_vcomm(); | ||
477 | config_dma(); | ||
478 | config_ppi(); | ||
479 | |||
480 | /* start dma */ | ||
481 | enable_dma(CH_PPI); | ||
482 | SSYNC(); | ||
483 | bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN); | ||
484 | SSYNC(); | ||
485 | |||
486 | if (!t_conf_done) { | ||
487 | config_timers(); | ||
488 | start_timers(); | ||
489 | } | ||
490 | /* gpio_set_value(MOD,1); */ | ||
491 | } | ||
492 | |||
493 | return 0; | ||
494 | } | ||
495 | |||
496 | static int bfin_lq035_fb_release(struct fb_info *info, int user) | ||
497 | { | ||
498 | unsigned long flags; | ||
499 | |||
500 | spin_lock_irqsave(&bfin_lq035_lock, flags); | ||
501 | lq035_open_cnt--; | ||
502 | spin_unlock_irqrestore(&bfin_lq035_lock, flags); | ||
503 | |||
504 | |||
505 | if (lq035_open_cnt <= 0) { | ||
506 | |||
507 | bfin_write_PPI_CONTROL(0); | ||
508 | SSYNC(); | ||
509 | |||
510 | disable_dma(CH_PPI); | ||
511 | } | ||
512 | |||
513 | return 0; | ||
514 | } | ||
515 | |||
516 | |||
517 | static int bfin_lq035_fb_check_var(struct fb_var_screeninfo *var, | ||
518 | struct fb_info *info) | ||
519 | { | ||
520 | switch (var->bits_per_pixel) { | ||
521 | case 16:/* DIRECTCOLOUR, 64k */ | ||
522 | var->red.offset = info->var.red.offset; | ||
523 | var->green.offset = info->var.green.offset; | ||
524 | var->blue.offset = info->var.blue.offset; | ||
525 | var->red.length = info->var.red.length; | ||
526 | var->green.length = info->var.green.length; | ||
527 | var->blue.length = info->var.blue.length; | ||
528 | var->transp.offset = 0; | ||
529 | var->transp.length = 0; | ||
530 | var->transp.msb_right = 0; | ||
531 | var->red.msb_right = 0; | ||
532 | var->green.msb_right = 0; | ||
533 | var->blue.msb_right = 0; | ||
534 | break; | ||
535 | default: | ||
536 | pr_debug("%s: depth not supported: %u BPP\n", __func__, | ||
537 | var->bits_per_pixel); | ||
538 | return -EINVAL; | ||
539 | } | ||
540 | |||
541 | if (info->var.xres != var->xres || | ||
542 | info->var.yres != var->yres || | ||
543 | info->var.xres_virtual != var->xres_virtual || | ||
544 | info->var.yres_virtual != var->yres_virtual) { | ||
545 | pr_debug("%s: Resolution not supported: X%u x Y%u\n", | ||
546 | __func__, var->xres, var->yres); | ||
547 | return -EINVAL; | ||
548 | } | ||
549 | |||
550 | /* | ||
551 | * Memory limit | ||
552 | */ | ||
553 | |||
554 | if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) { | ||
555 | pr_debug("%s: Memory Limit requested yres_virtual = %u\n", | ||
556 | __func__, var->yres_virtual); | ||
557 | return -ENOMEM; | ||
558 | } | ||
559 | |||
560 | return 0; | ||
561 | } | ||
562 | |||
563 | /* fb_rotate | ||
564 | * Rotate the display of this angle. This doesn't seems to be used by the core, | ||
565 | * but as our hardware supports it, so why not implementing it... | ||
566 | */ | ||
567 | static void bfin_lq035_fb_rotate(struct fb_info *fbi, int angle) | ||
568 | { | ||
569 | pr_debug("%s: %p %d", __func__, fbi, angle); | ||
570 | #if (defined(UD) && defined(LBR)) | ||
571 | switch (angle) { | ||
572 | |||
573 | case 180: | ||
574 | gpio_set_value(LBR, 0); | ||
575 | gpio_set_value(UD, 1); | ||
576 | break; | ||
577 | default: | ||
578 | gpio_set_value(LBR, 1); | ||
579 | gpio_set_value(UD, 0); | ||
580 | break; | ||
581 | } | ||
582 | #endif | ||
583 | } | ||
584 | |||
585 | static int bfin_lq035_fb_cursor(struct fb_info *info, struct fb_cursor *cursor) | ||
586 | { | ||
587 | if (nocursor) | ||
588 | return 0; | ||
589 | else | ||
590 | return -EINVAL; /* just to force soft_cursor() call */ | ||
591 | } | ||
592 | |||
593 | static int bfin_lq035_fb_setcolreg(u_int regno, u_int red, u_int green, | ||
594 | u_int blue, u_int transp, | ||
595 | struct fb_info *info) | ||
596 | { | ||
597 | if (regno >= NBR_PALETTE) | ||
598 | return -EINVAL; | ||
599 | |||
600 | if (info->var.grayscale) | ||
601 | /* grayscale = 0.30*R + 0.59*G + 0.11*B */ | ||
602 | red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; | ||
603 | |||
604 | if (info->fix.visual == FB_VISUAL_TRUECOLOR) { | ||
605 | |||
606 | u32 value; | ||
607 | /* Place color in the pseudopalette */ | ||
608 | if (regno > 16) | ||
609 | return -EINVAL; | ||
610 | |||
611 | red >>= (16 - info->var.red.length); | ||
612 | green >>= (16 - info->var.green.length); | ||
613 | blue >>= (16 - info->var.blue.length); | ||
614 | |||
615 | value = (red << info->var.red.offset) | | ||
616 | (green << info->var.green.offset)| | ||
617 | (blue << info->var.blue.offset); | ||
618 | value &= 0xFFFF; | ||
619 | |||
620 | ((u32 *) (info->pseudo_palette))[regno] = value; | ||
621 | |||
622 | } | ||
623 | |||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | static struct fb_ops bfin_lq035_fb_ops = { | ||
628 | .owner = THIS_MODULE, | ||
629 | .fb_open = bfin_lq035_fb_open, | ||
630 | .fb_release = bfin_lq035_fb_release, | ||
631 | .fb_check_var = bfin_lq035_fb_check_var, | ||
632 | .fb_rotate = bfin_lq035_fb_rotate, | ||
633 | .fb_fillrect = cfb_fillrect, | ||
634 | .fb_copyarea = cfb_copyarea, | ||
635 | .fb_imageblit = cfb_imageblit, | ||
636 | .fb_cursor = bfin_lq035_fb_cursor, | ||
637 | .fb_setcolreg = bfin_lq035_fb_setcolreg, | ||
638 | }; | ||
639 | |||
640 | static int bl_get_brightness(struct backlight_device *bd) | ||
641 | { | ||
642 | return current_brightness; | ||
643 | } | ||
644 | |||
645 | static const struct backlight_ops bfin_lq035fb_bl_ops = { | ||
646 | .get_brightness = bl_get_brightness, | ||
647 | }; | ||
648 | |||
649 | static struct backlight_device *bl_dev; | ||
650 | |||
651 | static int bfin_lcd_get_power(struct lcd_device *dev) | ||
652 | { | ||
653 | return 0; | ||
654 | } | ||
655 | |||
656 | static int bfin_lcd_set_power(struct lcd_device *dev, int power) | ||
657 | { | ||
658 | return 0; | ||
659 | } | ||
660 | |||
661 | static int bfin_lcd_get_contrast(struct lcd_device *dev) | ||
662 | { | ||
663 | return (int)vcomm_value; | ||
664 | } | ||
665 | |||
666 | static int bfin_lcd_set_contrast(struct lcd_device *dev, int contrast) | ||
667 | { | ||
668 | if (contrast > 255) | ||
669 | contrast = 255; | ||
670 | if (contrast < 0) | ||
671 | contrast = 0; | ||
672 | |||
673 | vcomm_value = (unsigned char)contrast; | ||
674 | set_vcomm(); | ||
675 | return 0; | ||
676 | } | ||
677 | |||
678 | static int bfin_lcd_check_fb(struct lcd_device *lcd, struct fb_info *fi) | ||
679 | { | ||
680 | if (!fi || (fi == &bfin_lq035_fb)) | ||
681 | return 1; | ||
682 | return 0; | ||
683 | } | ||
684 | |||
685 | static struct lcd_ops bfin_lcd_ops = { | ||
686 | .get_power = bfin_lcd_get_power, | ||
687 | .set_power = bfin_lcd_set_power, | ||
688 | .get_contrast = bfin_lcd_get_contrast, | ||
689 | .set_contrast = bfin_lcd_set_contrast, | ||
690 | .check_fb = bfin_lcd_check_fb, | ||
691 | }; | ||
692 | |||
693 | static struct lcd_device *lcd_dev; | ||
694 | |||
695 | static int __devinit bfin_lq035_probe(struct platform_device *pdev) | ||
696 | { | ||
697 | struct backlight_properties props; | ||
698 | dma_addr_t dma_handle; | ||
699 | |||
700 | if (request_dma(CH_PPI, KBUILD_MODNAME)) { | ||
701 | pr_err("couldn't request PPI DMA\n"); | ||
702 | return -EFAULT; | ||
703 | } | ||
704 | |||
705 | if (request_ports()) { | ||
706 | pr_err("couldn't request gpio port\n"); | ||
707 | free_dma(CH_PPI); | ||
708 | return -EFAULT; | ||
709 | } | ||
710 | |||
711 | fb_buffer = dma_alloc_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, | ||
712 | &dma_handle, GFP_KERNEL); | ||
713 | if (fb_buffer == NULL) { | ||
714 | pr_err("couldn't allocate dma buffer\n"); | ||
715 | free_dma(CH_PPI); | ||
716 | free_ports(); | ||
717 | return -ENOMEM; | ||
718 | } | ||
719 | |||
720 | if (L1_DATA_A_LENGTH) | ||
721 | dma_desc_table = l1_data_sram_zalloc(TOTAL_DMA_DESC_SIZE); | ||
722 | else | ||
723 | dma_desc_table = dma_alloc_coherent(NULL, TOTAL_DMA_DESC_SIZE, | ||
724 | &dma_handle, 0); | ||
725 | |||
726 | if (dma_desc_table == NULL) { | ||
727 | pr_err("couldn't allocate dma descriptor\n"); | ||
728 | free_dma(CH_PPI); | ||
729 | free_ports(); | ||
730 | dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); | ||
731 | return -ENOMEM; | ||
732 | } | ||
733 | |||
734 | bfin_lq035_fb.screen_base = (void *)fb_buffer; | ||
735 | bfin_lq035_fb_fix.smem_start = (int)fb_buffer; | ||
736 | if (landscape) { | ||
737 | bfin_lq035_fb_defined.xres = LCD_Y_RES; | ||
738 | bfin_lq035_fb_defined.yres = LCD_X_RES; | ||
739 | bfin_lq035_fb_defined.xres_virtual = LCD_Y_RES; | ||
740 | bfin_lq035_fb_defined.yres_virtual = LCD_X_RES; | ||
741 | |||
742 | bfin_lq035_fb_fix.line_length = LCD_Y_RES*(LCD_BBP/8); | ||
743 | } else { | ||
744 | bfin_lq035_fb.screen_base += ACTIVE_VIDEO_MEM_OFFSET; | ||
745 | bfin_lq035_fb_fix.smem_start += ACTIVE_VIDEO_MEM_OFFSET; | ||
746 | } | ||
747 | |||
748 | bfin_lq035_fb_defined.green.msb_right = 0; | ||
749 | bfin_lq035_fb_defined.red.msb_right = 0; | ||
750 | bfin_lq035_fb_defined.blue.msb_right = 0; | ||
751 | bfin_lq035_fb_defined.green.offset = 5; | ||
752 | bfin_lq035_fb_defined.green.length = 6; | ||
753 | bfin_lq035_fb_defined.red.length = 5; | ||
754 | bfin_lq035_fb_defined.blue.length = 5; | ||
755 | |||
756 | if (bgr) { | ||
757 | bfin_lq035_fb_defined.red.offset = 0; | ||
758 | bfin_lq035_fb_defined.blue.offset = 11; | ||
759 | } else { | ||
760 | bfin_lq035_fb_defined.red.offset = 11; | ||
761 | bfin_lq035_fb_defined.blue.offset = 0; | ||
762 | } | ||
763 | |||
764 | bfin_lq035_fb.fbops = &bfin_lq035_fb_ops; | ||
765 | bfin_lq035_fb.var = bfin_lq035_fb_defined; | ||
766 | |||
767 | bfin_lq035_fb.fix = bfin_lq035_fb_fix; | ||
768 | bfin_lq035_fb.flags = FBINFO_DEFAULT; | ||
769 | |||
770 | |||
771 | bfin_lq035_fb.pseudo_palette = kzalloc(sizeof(u32) * 16, GFP_KERNEL); | ||
772 | if (bfin_lq035_fb.pseudo_palette == NULL) { | ||
773 | pr_err("failed to allocate pseudo_palette\n"); | ||
774 | free_dma(CH_PPI); | ||
775 | free_ports(); | ||
776 | dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); | ||
777 | return -ENOMEM; | ||
778 | } | ||
779 | |||
780 | if (fb_alloc_cmap(&bfin_lq035_fb.cmap, NBR_PALETTE, 0) < 0) { | ||
781 | pr_err("failed to allocate colormap (%d entries)\n", | ||
782 | NBR_PALETTE); | ||
783 | free_dma(CH_PPI); | ||
784 | free_ports(); | ||
785 | dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); | ||
786 | kfree(bfin_lq035_fb.pseudo_palette); | ||
787 | return -EFAULT; | ||
788 | } | ||
789 | |||
790 | if (register_framebuffer(&bfin_lq035_fb) < 0) { | ||
791 | pr_err("unable to register framebuffer\n"); | ||
792 | free_dma(CH_PPI); | ||
793 | free_ports(); | ||
794 | dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); | ||
795 | fb_buffer = NULL; | ||
796 | kfree(bfin_lq035_fb.pseudo_palette); | ||
797 | fb_dealloc_cmap(&bfin_lq035_fb.cmap); | ||
798 | return -EINVAL; | ||
799 | } | ||
800 | |||
801 | i2c_add_driver(&ad5280_driver); | ||
802 | |||
803 | memset(&props, 0, sizeof(props)); | ||
804 | props.max_brightness = MAX_BRIGHENESS; | ||
805 | bl_dev = backlight_device_register("bf537-bl", NULL, NULL, | ||
806 | &bfin_lq035fb_bl_ops, &props); | ||
807 | |||
808 | lcd_dev = lcd_device_register(KBUILD_MODNAME, &pdev->dev, NULL, | ||
809 | &bfin_lcd_ops); | ||
810 | lcd_dev->props.max_contrast = 255, | ||
811 | |||
812 | pr_info("initialized"); | ||
813 | |||
814 | return 0; | ||
815 | } | ||
816 | |||
817 | static int __devexit bfin_lq035_remove(struct platform_device *pdev) | ||
818 | { | ||
819 | if (fb_buffer != NULL) | ||
820 | dma_free_coherent(NULL, TOTAL_VIDEO_MEM_SIZE, fb_buffer, 0); | ||
821 | |||
822 | if (L1_DATA_A_LENGTH) | ||
823 | l1_data_sram_free(dma_desc_table); | ||
824 | else | ||
825 | dma_free_coherent(NULL, TOTAL_DMA_DESC_SIZE, NULL, 0); | ||
826 | |||
827 | bfin_write_TIMER_DISABLE(TIMEN_SP|TIMEN_SPS|TIMEN_PS_CLS| | ||
828 | TIMEN_LP|TIMEN_REV); | ||
829 | t_conf_done = 0; | ||
830 | |||
831 | free_dma(CH_PPI); | ||
832 | |||
833 | |||
834 | kfree(bfin_lq035_fb.pseudo_palette); | ||
835 | fb_dealloc_cmap(&bfin_lq035_fb.cmap); | ||
836 | |||
837 | |||
838 | lcd_device_unregister(lcd_dev); | ||
839 | backlight_device_unregister(bl_dev); | ||
840 | |||
841 | unregister_framebuffer(&bfin_lq035_fb); | ||
842 | i2c_del_driver(&ad5280_driver); | ||
843 | |||
844 | free_ports(); | ||
845 | |||
846 | pr_info("unregistered LCD driver\n"); | ||
847 | |||
848 | return 0; | ||
849 | } | ||
850 | |||
851 | #ifdef CONFIG_PM | ||
852 | static int bfin_lq035_suspend(struct platform_device *pdev, pm_message_t state) | ||
853 | { | ||
854 | if (lq035_open_cnt > 0) { | ||
855 | bfin_write_PPI_CONTROL(0); | ||
856 | SSYNC(); | ||
857 | disable_dma(CH_PPI); | ||
858 | } | ||
859 | |||
860 | return 0; | ||
861 | } | ||
862 | |||
863 | static int bfin_lq035_resume(struct platform_device *pdev) | ||
864 | { | ||
865 | if (lq035_open_cnt > 0) { | ||
866 | bfin_write_PPI_CONTROL(0); | ||
867 | SSYNC(); | ||
868 | |||
869 | config_dma(); | ||
870 | config_ppi(); | ||
871 | |||
872 | enable_dma(CH_PPI); | ||
873 | bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN); | ||
874 | SSYNC(); | ||
875 | |||
876 | config_timers(); | ||
877 | start_timers(); | ||
878 | } else { | ||
879 | t_conf_done = 0; | ||
880 | } | ||
881 | |||
882 | return 0; | ||
883 | } | ||
884 | #else | ||
885 | # define bfin_lq035_suspend NULL | ||
886 | # define bfin_lq035_resume NULL | ||
887 | #endif | ||
888 | |||
889 | static struct platform_driver bfin_lq035_driver = { | ||
890 | .probe = bfin_lq035_probe, | ||
891 | .remove = __devexit_p(bfin_lq035_remove), | ||
892 | .suspend = bfin_lq035_suspend, | ||
893 | .resume = bfin_lq035_resume, | ||
894 | .driver = { | ||
895 | .name = KBUILD_MODNAME, | ||
896 | .owner = THIS_MODULE, | ||
897 | }, | ||
898 | }; | ||
899 | |||
900 | static int __init bfin_lq035_driver_init(void) | ||
901 | { | ||
902 | request_module("i2c-bfin-twi"); | ||
903 | return platform_driver_register(&bfin_lq035_driver); | ||
904 | } | ||
905 | module_init(bfin_lq035_driver_init); | ||
906 | |||
907 | static void __exit bfin_lq035_driver_cleanup(void) | ||
908 | { | ||
909 | platform_driver_unregister(&bfin_lq035_driver); | ||
910 | } | ||
911 | module_exit(bfin_lq035_driver_cleanup); | ||
912 | |||
913 | MODULE_DESCRIPTION("SHARP LQ035Q7DB03 TFT LCD Driver"); | ||
914 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/bfin_adv7393fb.c b/drivers/video/bfin_adv7393fb.c new file mode 100644 index 000000000000..8486f541156b --- /dev/null +++ b/drivers/video/bfin_adv7393fb.c | |||
@@ -0,0 +1,832 @@ | |||
1 | /* | ||
2 | * Frame buffer driver for ADV7393/2 video encoder | ||
3 | * | ||
4 | * Copyright 2006-2009 Analog Devices Inc. | ||
5 | * Licensed under the GPL-2 or late. | ||
6 | */ | ||
7 | |||
8 | /* | ||
9 | * TODO: Remove Globals | ||
10 | * TODO: Code Cleanup | ||
11 | */ | ||
12 | |||
13 | #define pr_fmt(fmt) DRIVER_NAME ": " fmt | ||
14 | |||
15 | #include <linux/module.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/string.h> | ||
19 | #include <linux/mm.h> | ||
20 | #include <linux/tty.h> | ||
21 | #include <linux/slab.h> | ||
22 | #include <linux/delay.h> | ||
23 | #include <linux/fb.h> | ||
24 | #include <linux/ioport.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/types.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | #include <linux/sched.h> | ||
29 | #include <asm/blackfin.h> | ||
30 | #include <asm/irq.h> | ||
31 | #include <asm/dma.h> | ||
32 | #include <linux/uaccess.h> | ||
33 | #include <linux/gpio.h> | ||
34 | #include <asm/portmux.h> | ||
35 | |||
36 | #include <linux/dma-mapping.h> | ||
37 | #include <linux/proc_fs.h> | ||
38 | #include <linux/platform_device.h> | ||
39 | |||
40 | #include <linux/i2c.h> | ||
41 | #include <linux/i2c-dev.h> | ||
42 | |||
43 | #include "bfin_adv7393fb.h" | ||
44 | |||
45 | static int mode = VMODE; | ||
46 | static int mem = VMEM; | ||
47 | static int nocursor = 1; | ||
48 | |||
49 | static const unsigned short ppi_pins[] = { | ||
50 | P_PPI0_CLK, P_PPI0_FS1, P_PPI0_FS2, | ||
51 | P_PPI0_D0, P_PPI0_D1, P_PPI0_D2, P_PPI0_D3, | ||
52 | P_PPI0_D4, P_PPI0_D5, P_PPI0_D6, P_PPI0_D7, | ||
53 | P_PPI0_D8, P_PPI0_D9, P_PPI0_D10, P_PPI0_D11, | ||
54 | P_PPI0_D12, P_PPI0_D13, P_PPI0_D14, P_PPI0_D15, | ||
55 | 0 | ||
56 | }; | ||
57 | |||
58 | /* | ||
59 | * card parameters | ||
60 | */ | ||
61 | |||
62 | static struct bfin_adv7393_fb_par { | ||
63 | /* structure holding blackfin / adv7393 paramters when | ||
64 | screen is blanked */ | ||
65 | struct { | ||
66 | u8 Mode; /* ntsc/pal/? */ | ||
67 | } vga_state; | ||
68 | atomic_t ref_count; | ||
69 | } bfin_par; | ||
70 | |||
71 | /* --------------------------------------------------------------------- */ | ||
72 | |||
73 | static struct fb_var_screeninfo bfin_adv7393_fb_defined = { | ||
74 | .xres = 720, | ||
75 | .yres = 480, | ||
76 | .xres_virtual = 720, | ||
77 | .yres_virtual = 480, | ||
78 | .bits_per_pixel = 16, | ||
79 | .activate = FB_ACTIVATE_TEST, | ||
80 | .height = -1, | ||
81 | .width = -1, | ||
82 | .left_margin = 0, | ||
83 | .right_margin = 0, | ||
84 | .upper_margin = 0, | ||
85 | .lower_margin = 0, | ||
86 | .vmode = FB_VMODE_INTERLACED, | ||
87 | .red = {11, 5, 0}, | ||
88 | .green = {5, 6, 0}, | ||
89 | .blue = {0, 5, 0}, | ||
90 | .transp = {0, 0, 0}, | ||
91 | }; | ||
92 | |||
93 | static struct fb_fix_screeninfo bfin_adv7393_fb_fix __devinitdata = { | ||
94 | .id = "BFIN ADV7393", | ||
95 | .smem_len = 720 * 480 * 2, | ||
96 | .type = FB_TYPE_PACKED_PIXELS, | ||
97 | .visual = FB_VISUAL_TRUECOLOR, | ||
98 | .xpanstep = 0, | ||
99 | .ypanstep = 0, | ||
100 | .line_length = 720 * 2, | ||
101 | .accel = FB_ACCEL_NONE | ||
102 | }; | ||
103 | |||
104 | static struct fb_ops bfin_adv7393_fb_ops = { | ||
105 | .owner = THIS_MODULE, | ||
106 | .fb_open = bfin_adv7393_fb_open, | ||
107 | .fb_release = bfin_adv7393_fb_release, | ||
108 | .fb_check_var = bfin_adv7393_fb_check_var, | ||
109 | .fb_pan_display = bfin_adv7393_fb_pan_display, | ||
110 | .fb_blank = bfin_adv7393_fb_blank, | ||
111 | .fb_fillrect = cfb_fillrect, | ||
112 | .fb_copyarea = cfb_copyarea, | ||
113 | .fb_imageblit = cfb_imageblit, | ||
114 | .fb_cursor = bfin_adv7393_fb_cursor, | ||
115 | .fb_setcolreg = bfin_adv7393_fb_setcolreg, | ||
116 | }; | ||
117 | |||
118 | static int dma_desc_list(struct adv7393fb_device *fbdev, u16 arg) | ||
119 | { | ||
120 | if (arg == BUILD) { /* Build */ | ||
121 | fbdev->vb1 = l1_data_sram_zalloc(sizeof(struct dmasg)); | ||
122 | if (fbdev->vb1 == NULL) | ||
123 | goto error; | ||
124 | |||
125 | fbdev->av1 = l1_data_sram_zalloc(sizeof(struct dmasg)); | ||
126 | if (fbdev->av1 == NULL) | ||
127 | goto error; | ||
128 | |||
129 | fbdev->vb2 = l1_data_sram_zalloc(sizeof(struct dmasg)); | ||
130 | if (fbdev->vb2 == NULL) | ||
131 | goto error; | ||
132 | |||
133 | fbdev->av2 = l1_data_sram_zalloc(sizeof(struct dmasg)); | ||
134 | if (fbdev->av2 == NULL) | ||
135 | goto error; | ||
136 | |||
137 | /* Build linked DMA descriptor list */ | ||
138 | fbdev->vb1->next_desc_addr = fbdev->av1; | ||
139 | fbdev->av1->next_desc_addr = fbdev->vb2; | ||
140 | fbdev->vb2->next_desc_addr = fbdev->av2; | ||
141 | fbdev->av2->next_desc_addr = fbdev->vb1; | ||
142 | |||
143 | /* Save list head */ | ||
144 | fbdev->descriptor_list_head = fbdev->av2; | ||
145 | |||
146 | /* Vertical Blanking Field 1 */ | ||
147 | fbdev->vb1->start_addr = VB_DUMMY_MEMORY_SOURCE; | ||
148 | fbdev->vb1->cfg = DMA_CFG_VAL; | ||
149 | |||
150 | fbdev->vb1->x_count = | ||
151 | fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank; | ||
152 | |||
153 | fbdev->vb1->x_modify = 0; | ||
154 | fbdev->vb1->y_count = fbdev->modes[mode].vb1_lines; | ||
155 | fbdev->vb1->y_modify = 0; | ||
156 | |||
157 | /* Active Video Field 1 */ | ||
158 | |||
159 | fbdev->av1->start_addr = (unsigned long)fbdev->fb_mem; | ||
160 | fbdev->av1->cfg = DMA_CFG_VAL; | ||
161 | fbdev->av1->x_count = | ||
162 | fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank; | ||
163 | fbdev->av1->x_modify = fbdev->modes[mode].bpp / 8; | ||
164 | fbdev->av1->y_count = fbdev->modes[mode].a_lines; | ||
165 | fbdev->av1->y_modify = | ||
166 | (fbdev->modes[mode].xres - fbdev->modes[mode].boeft_blank + | ||
167 | 1) * (fbdev->modes[mode].bpp / 8); | ||
168 | |||
169 | /* Vertical Blanking Field 2 */ | ||
170 | |||
171 | fbdev->vb2->start_addr = VB_DUMMY_MEMORY_SOURCE; | ||
172 | fbdev->vb2->cfg = DMA_CFG_VAL; | ||
173 | fbdev->vb2->x_count = | ||
174 | fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank; | ||
175 | |||
176 | fbdev->vb2->x_modify = 0; | ||
177 | fbdev->vb2->y_count = fbdev->modes[mode].vb2_lines; | ||
178 | fbdev->vb2->y_modify = 0; | ||
179 | |||
180 | /* Active Video Field 2 */ | ||
181 | |||
182 | fbdev->av2->start_addr = | ||
183 | (unsigned long)fbdev->fb_mem + fbdev->line_len; | ||
184 | |||
185 | fbdev->av2->cfg = DMA_CFG_VAL; | ||
186 | |||
187 | fbdev->av2->x_count = | ||
188 | fbdev->modes[mode].xres + fbdev->modes[mode].boeft_blank; | ||
189 | |||
190 | fbdev->av2->x_modify = (fbdev->modes[mode].bpp / 8); | ||
191 | fbdev->av2->y_count = fbdev->modes[mode].a_lines; | ||
192 | |||
193 | fbdev->av2->y_modify = | ||
194 | (fbdev->modes[mode].xres - fbdev->modes[mode].boeft_blank + | ||
195 | 1) * (fbdev->modes[mode].bpp / 8); | ||
196 | |||
197 | return 1; | ||
198 | } | ||
199 | |||
200 | error: | ||
201 | l1_data_sram_free(fbdev->vb1); | ||
202 | l1_data_sram_free(fbdev->av1); | ||
203 | l1_data_sram_free(fbdev->vb2); | ||
204 | l1_data_sram_free(fbdev->av2); | ||
205 | |||
206 | return 0; | ||
207 | } | ||
208 | |||
209 | static int bfin_config_dma(struct adv7393fb_device *fbdev) | ||
210 | { | ||
211 | BUG_ON(!(fbdev->fb_mem)); | ||
212 | |||
213 | set_dma_x_count(CH_PPI, fbdev->descriptor_list_head->x_count); | ||
214 | set_dma_x_modify(CH_PPI, fbdev->descriptor_list_head->x_modify); | ||
215 | set_dma_y_count(CH_PPI, fbdev->descriptor_list_head->y_count); | ||
216 | set_dma_y_modify(CH_PPI, fbdev->descriptor_list_head->y_modify); | ||
217 | set_dma_start_addr(CH_PPI, fbdev->descriptor_list_head->start_addr); | ||
218 | set_dma_next_desc_addr(CH_PPI, | ||
219 | fbdev->descriptor_list_head->next_desc_addr); | ||
220 | set_dma_config(CH_PPI, fbdev->descriptor_list_head->cfg); | ||
221 | |||
222 | return 1; | ||
223 | } | ||
224 | |||
225 | static void bfin_disable_dma(void) | ||
226 | { | ||
227 | bfin_write_DMA0_CONFIG(bfin_read_DMA0_CONFIG() & ~DMAEN); | ||
228 | } | ||
229 | |||
230 | static void bfin_config_ppi(struct adv7393fb_device *fbdev) | ||
231 | { | ||
232 | if (ANOMALY_05000183) { | ||
233 | bfin_write_TIMER2_CONFIG(WDTH_CAP); | ||
234 | bfin_write_TIMER_ENABLE(TIMEN2); | ||
235 | } | ||
236 | |||
237 | bfin_write_PPI_CONTROL(0x381E); | ||
238 | bfin_write_PPI_FRAME(fbdev->modes[mode].tot_lines); | ||
239 | bfin_write_PPI_COUNT(fbdev->modes[mode].xres + | ||
240 | fbdev->modes[mode].boeft_blank - 1); | ||
241 | bfin_write_PPI_DELAY(fbdev->modes[mode].aoeft_blank - 1); | ||
242 | } | ||
243 | |||
244 | static void bfin_enable_ppi(void) | ||
245 | { | ||
246 | bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() | PORT_EN); | ||
247 | } | ||
248 | |||
249 | static void bfin_disable_ppi(void) | ||
250 | { | ||
251 | bfin_write_PPI_CONTROL(bfin_read_PPI_CONTROL() & ~PORT_EN); | ||
252 | } | ||
253 | |||
254 | static inline int adv7393_write(struct i2c_client *client, u8 reg, u8 value) | ||
255 | { | ||
256 | return i2c_smbus_write_byte_data(client, reg, value); | ||
257 | } | ||
258 | |||
259 | static inline int adv7393_read(struct i2c_client *client, u8 reg) | ||
260 | { | ||
261 | return i2c_smbus_read_byte_data(client, reg); | ||
262 | } | ||
263 | |||
264 | static int | ||
265 | adv7393_write_block(struct i2c_client *client, | ||
266 | const u8 *data, unsigned int len) | ||
267 | { | ||
268 | int ret = -1; | ||
269 | u8 reg; | ||
270 | |||
271 | while (len >= 2) { | ||
272 | reg = *data++; | ||
273 | ret = adv7393_write(client, reg, *data++); | ||
274 | if (ret < 0) | ||
275 | break; | ||
276 | len -= 2; | ||
277 | } | ||
278 | |||
279 | return ret; | ||
280 | } | ||
281 | |||
282 | static int adv7393_mode(struct i2c_client *client, u16 mode) | ||
283 | { | ||
284 | switch (mode) { | ||
285 | case POWER_ON: /* ADV7393 Sleep mode OFF */ | ||
286 | adv7393_write(client, 0x00, 0x1E); | ||
287 | break; | ||
288 | case POWER_DOWN: /* ADV7393 Sleep mode ON */ | ||
289 | adv7393_write(client, 0x00, 0x1F); | ||
290 | break; | ||
291 | case BLANK_OFF: /* Pixel Data Valid */ | ||
292 | adv7393_write(client, 0x82, 0xCB); | ||
293 | break; | ||
294 | case BLANK_ON: /* Pixel Data Invalid */ | ||
295 | adv7393_write(client, 0x82, 0x8B); | ||
296 | break; | ||
297 | default: | ||
298 | return -EINVAL; | ||
299 | break; | ||
300 | } | ||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | static irqreturn_t ppi_irq_error(int irq, void *dev_id) | ||
305 | { | ||
306 | |||
307 | struct adv7393fb_device *fbdev = (struct adv7393fb_device *)dev_id; | ||
308 | |||
309 | u16 status = bfin_read_PPI_STATUS(); | ||
310 | |||
311 | pr_debug("%s: PPI Status = 0x%X\n", __func__, status); | ||
312 | |||
313 | if (status) { | ||
314 | bfin_disable_dma(); /* TODO: Check Sequence */ | ||
315 | bfin_disable_ppi(); | ||
316 | bfin_clear_PPI_STATUS(); | ||
317 | bfin_config_dma(fbdev); | ||
318 | bfin_enable_ppi(); | ||
319 | } | ||
320 | |||
321 | return IRQ_HANDLED; | ||
322 | |||
323 | } | ||
324 | |||
325 | static int proc_output(char *buf) | ||
326 | { | ||
327 | char *p = buf; | ||
328 | |||
329 | p += sprintf(p, | ||
330 | "Usage:\n" | ||
331 | "echo 0x[REG][Value] > adv7393\n" | ||
332 | "example: echo 0x1234 >adv7393\n" | ||
333 | "writes 0x34 into Register 0x12\n"); | ||
334 | |||
335 | return p - buf; | ||
336 | } | ||
337 | |||
338 | static int | ||
339 | adv7393_read_proc(char *page, char **start, off_t off, | ||
340 | int count, int *eof, void *data) | ||
341 | { | ||
342 | int len; | ||
343 | |||
344 | len = proc_output(page); | ||
345 | if (len <= off + count) | ||
346 | *eof = 1; | ||
347 | *start = page + off; | ||
348 | len -= off; | ||
349 | if (len > count) | ||
350 | len = count; | ||
351 | if (len < 0) | ||
352 | len = 0; | ||
353 | return len; | ||
354 | } | ||
355 | |||
356 | static int | ||
357 | adv7393_write_proc(struct file *file, const char __user * buffer, | ||
358 | unsigned long count, void *data) | ||
359 | { | ||
360 | struct adv7393fb_device *fbdev = data; | ||
361 | char line[8]; | ||
362 | unsigned int val; | ||
363 | int ret; | ||
364 | |||
365 | ret = copy_from_user(line, buffer, count); | ||
366 | if (ret) | ||
367 | return -EFAULT; | ||
368 | |||
369 | val = simple_strtoul(line, NULL, 0); | ||
370 | adv7393_write(fbdev->client, val >> 8, val & 0xff); | ||
371 | |||
372 | return count; | ||
373 | } | ||
374 | |||
375 | static int __devinit bfin_adv7393_fb_probe(struct i2c_client *client, | ||
376 | const struct i2c_device_id *id) | ||
377 | { | ||
378 | int ret = 0; | ||
379 | struct proc_dir_entry *entry; | ||
380 | int num_modes = ARRAY_SIZE(known_modes); | ||
381 | |||
382 | struct adv7393fb_device *fbdev = NULL; | ||
383 | |||
384 | if (mem > 2) { | ||
385 | dev_err(&client->dev, "mem out of allowed range [1;2]\n"); | ||
386 | return -EINVAL; | ||
387 | } | ||
388 | |||
389 | if (mode > num_modes) { | ||
390 | dev_err(&client->dev, "mode %d: not supported", mode); | ||
391 | return -EFAULT; | ||
392 | } | ||
393 | |||
394 | fbdev = kzalloc(sizeof(*fbdev), GFP_KERNEL); | ||
395 | if (!fbdev) { | ||
396 | dev_err(&client->dev, "failed to allocate device private record"); | ||
397 | return -ENOMEM; | ||
398 | } | ||
399 | |||
400 | i2c_set_clientdata(client, fbdev); | ||
401 | |||
402 | fbdev->modes = known_modes; | ||
403 | fbdev->client = client; | ||
404 | |||
405 | fbdev->fb_len = | ||
406 | mem * fbdev->modes[mode].xres * fbdev->modes[mode].xres * | ||
407 | (fbdev->modes[mode].bpp / 8); | ||
408 | |||
409 | fbdev->line_len = | ||
410 | fbdev->modes[mode].xres * (fbdev->modes[mode].bpp / 8); | ||
411 | |||
412 | /* Workaround "PPI Does Not Start Properly In Specific Mode" */ | ||
413 | if (ANOMALY_05000400) { | ||
414 | if (gpio_request(P_IDENT(P_PPI0_FS3), "PPI0_FS3")) { | ||
415 | dev_err(&client->dev, "PPI0_FS3 GPIO request failed\n"); | ||
416 | ret = -EBUSY; | ||
417 | goto out_8; | ||
418 | } | ||
419 | gpio_direction_output(P_IDENT(P_PPI0_FS3), 0); | ||
420 | } | ||
421 | |||
422 | if (peripheral_request_list(ppi_pins, DRIVER_NAME)) { | ||
423 | dev_err(&client->dev, "requesting PPI peripheral failed\n"); | ||
424 | ret = -EFAULT; | ||
425 | goto out_8; | ||
426 | } | ||
427 | |||
428 | fbdev->fb_mem = | ||
429 | dma_alloc_coherent(NULL, fbdev->fb_len, &fbdev->dma_handle, | ||
430 | GFP_KERNEL); | ||
431 | |||
432 | if (NULL == fbdev->fb_mem) { | ||
433 | dev_err(&client->dev, "couldn't allocate dma buffer (%d bytes)\n", | ||
434 | (u32) fbdev->fb_len); | ||
435 | ret = -ENOMEM; | ||
436 | goto out_7; | ||
437 | } | ||
438 | |||
439 | fbdev->info.screen_base = (void *)fbdev->fb_mem; | ||
440 | bfin_adv7393_fb_fix.smem_start = (int)fbdev->fb_mem; | ||
441 | |||
442 | bfin_adv7393_fb_fix.smem_len = fbdev->fb_len; | ||
443 | bfin_adv7393_fb_fix.line_length = fbdev->line_len; | ||
444 | |||
445 | if (mem > 1) | ||
446 | bfin_adv7393_fb_fix.ypanstep = 1; | ||
447 | |||
448 | bfin_adv7393_fb_defined.red.length = 5; | ||
449 | bfin_adv7393_fb_defined.green.length = 6; | ||
450 | bfin_adv7393_fb_defined.blue.length = 5; | ||
451 | |||
452 | bfin_adv7393_fb_defined.xres = fbdev->modes[mode].xres; | ||
453 | bfin_adv7393_fb_defined.yres = fbdev->modes[mode].yres; | ||
454 | bfin_adv7393_fb_defined.xres_virtual = fbdev->modes[mode].xres; | ||
455 | bfin_adv7393_fb_defined.yres_virtual = mem * fbdev->modes[mode].yres; | ||
456 | bfin_adv7393_fb_defined.bits_per_pixel = fbdev->modes[mode].bpp; | ||
457 | |||
458 | fbdev->info.fbops = &bfin_adv7393_fb_ops; | ||
459 | fbdev->info.var = bfin_adv7393_fb_defined; | ||
460 | fbdev->info.fix = bfin_adv7393_fb_fix; | ||
461 | fbdev->info.par = &bfin_par; | ||
462 | fbdev->info.flags = FBINFO_DEFAULT; | ||
463 | |||
464 | fbdev->info.pseudo_palette = kzalloc(sizeof(u32) * 16, GFP_KERNEL); | ||
465 | if (!fbdev->info.pseudo_palette) { | ||
466 | dev_err(&client->dev, "failed to allocate pseudo_palette\n"); | ||
467 | ret = -ENOMEM; | ||
468 | goto out_6; | ||
469 | } | ||
470 | |||
471 | if (fb_alloc_cmap(&fbdev->info.cmap, BFIN_LCD_NBR_PALETTE_ENTRIES, 0) < 0) { | ||
472 | dev_err(&client->dev, "failed to allocate colormap (%d entries)\n", | ||
473 | BFIN_LCD_NBR_PALETTE_ENTRIES); | ||
474 | ret = -EFAULT; | ||
475 | goto out_5; | ||
476 | } | ||
477 | |||
478 | if (request_dma(CH_PPI, "BF5xx_PPI_DMA") < 0) { | ||
479 | dev_err(&client->dev, "unable to request PPI DMA\n"); | ||
480 | ret = -EFAULT; | ||
481 | goto out_4; | ||
482 | } | ||
483 | |||
484 | if (request_irq(IRQ_PPI_ERROR, ppi_irq_error, IRQF_DISABLED, | ||
485 | "PPI ERROR", fbdev) < 0) { | ||
486 | dev_err(&client->dev, "unable to request PPI ERROR IRQ\n"); | ||
487 | ret = -EFAULT; | ||
488 | goto out_3; | ||
489 | } | ||
490 | |||
491 | fbdev->open = 0; | ||
492 | |||
493 | ret = adv7393_write_block(client, fbdev->modes[mode].adv7393_i2c_initd, | ||
494 | fbdev->modes[mode].adv7393_i2c_initd_len); | ||
495 | |||
496 | if (ret) { | ||
497 | dev_err(&client->dev, "i2c attach: init error\n"); | ||
498 | goto out_1; | ||
499 | } | ||
500 | |||
501 | |||
502 | if (register_framebuffer(&fbdev->info) < 0) { | ||
503 | dev_err(&client->dev, "unable to register framebuffer\n"); | ||
504 | ret = -EFAULT; | ||
505 | goto out_1; | ||
506 | } | ||
507 | |||
508 | dev_info(&client->dev, "fb%d: %s frame buffer device\n", | ||
509 | fbdev->info.node, fbdev->info.fix.id); | ||
510 | dev_info(&client->dev, "fb memory address : 0x%p\n", fbdev->fb_mem); | ||
511 | |||
512 | entry = create_proc_entry("driver/adv7393", 0, NULL); | ||
513 | if (!entry) { | ||
514 | dev_err(&client->dev, "unable to create /proc entry\n"); | ||
515 | ret = -EFAULT; | ||
516 | goto out_0; | ||
517 | } | ||
518 | |||
519 | entry->read_proc = adv7393_read_proc; | ||
520 | entry->write_proc = adv7393_write_proc; | ||
521 | entry->data = fbdev; | ||
522 | |||
523 | return 0; | ||
524 | |||
525 | out_0: | ||
526 | unregister_framebuffer(&fbdev->info); | ||
527 | out_1: | ||
528 | free_irq(IRQ_PPI_ERROR, fbdev); | ||
529 | out_3: | ||
530 | free_dma(CH_PPI); | ||
531 | out_4: | ||
532 | dma_free_coherent(NULL, fbdev->fb_len, fbdev->fb_mem, | ||
533 | fbdev->dma_handle); | ||
534 | out_5: | ||
535 | fb_dealloc_cmap(&fbdev->info.cmap); | ||
536 | out_6: | ||
537 | kfree(fbdev->info.pseudo_palette); | ||
538 | out_7: | ||
539 | peripheral_free_list(ppi_pins); | ||
540 | out_8: | ||
541 | kfree(fbdev); | ||
542 | |||
543 | return ret; | ||
544 | } | ||
545 | |||
546 | static int bfin_adv7393_fb_open(struct fb_info *info, int user) | ||
547 | { | ||
548 | struct adv7393fb_device *fbdev = to_adv7393fb_device(info); | ||
549 | |||
550 | fbdev->info.screen_base = (void *)fbdev->fb_mem; | ||
551 | if (!fbdev->info.screen_base) { | ||
552 | dev_err(&fbdev->client->dev, "unable to map device\n"); | ||
553 | return -ENOMEM; | ||
554 | } | ||
555 | |||
556 | fbdev->open = 1; | ||
557 | dma_desc_list(fbdev, BUILD); | ||
558 | adv7393_mode(fbdev->client, BLANK_OFF); | ||
559 | bfin_config_ppi(fbdev); | ||
560 | bfin_config_dma(fbdev); | ||
561 | bfin_enable_ppi(); | ||
562 | |||
563 | return 0; | ||
564 | } | ||
565 | |||
566 | static int bfin_adv7393_fb_release(struct fb_info *info, int user) | ||
567 | { | ||
568 | struct adv7393fb_device *fbdev = to_adv7393fb_device(info); | ||
569 | |||
570 | adv7393_mode(fbdev->client, BLANK_ON); | ||
571 | bfin_disable_dma(); | ||
572 | bfin_disable_ppi(); | ||
573 | dma_desc_list(fbdev, DESTRUCT); | ||
574 | fbdev->open = 0; | ||
575 | return 0; | ||
576 | } | ||
577 | |||
578 | static int | ||
579 | bfin_adv7393_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | ||
580 | { | ||
581 | |||
582 | switch (var->bits_per_pixel) { | ||
583 | case 16:/* DIRECTCOLOUR, 64k */ | ||
584 | var->red.offset = info->var.red.offset; | ||
585 | var->green.offset = info->var.green.offset; | ||
586 | var->blue.offset = info->var.blue.offset; | ||
587 | var->red.length = info->var.red.length; | ||
588 | var->green.length = info->var.green.length; | ||
589 | var->blue.length = info->var.blue.length; | ||
590 | var->transp.offset = 0; | ||
591 | var->transp.length = 0; | ||
592 | var->transp.msb_right = 0; | ||
593 | var->red.msb_right = 0; | ||
594 | var->green.msb_right = 0; | ||
595 | var->blue.msb_right = 0; | ||
596 | break; | ||
597 | default: | ||
598 | pr_debug("%s: depth not supported: %u BPP\n", __func__, | ||
599 | var->bits_per_pixel); | ||
600 | return -EINVAL; | ||
601 | } | ||
602 | |||
603 | if (info->var.xres != var->xres || | ||
604 | info->var.yres != var->yres || | ||
605 | info->var.xres_virtual != var->xres_virtual || | ||
606 | info->var.yres_virtual != var->yres_virtual) { | ||
607 | pr_debug("%s: Resolution not supported: X%u x Y%u\n", | ||
608 | __func__, var->xres, var->yres); | ||
609 | return -EINVAL; | ||
610 | } | ||
611 | |||
612 | /* | ||
613 | * Memory limit | ||
614 | */ | ||
615 | |||
616 | if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) { | ||
617 | pr_debug("%s: Memory Limit requested yres_virtual = %u\n", | ||
618 | __func__, var->yres_virtual); | ||
619 | return -ENOMEM; | ||
620 | } | ||
621 | |||
622 | return 0; | ||
623 | } | ||
624 | |||
625 | static int | ||
626 | bfin_adv7393_fb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) | ||
627 | { | ||
628 | int dy; | ||
629 | u32 dmaaddr; | ||
630 | struct adv7393fb_device *fbdev = to_adv7393fb_device(info); | ||
631 | |||
632 | if (!var || !info) | ||
633 | return -EINVAL; | ||
634 | |||
635 | if (var->xoffset - info->var.xoffset) { | ||
636 | /* No support for X panning for now! */ | ||
637 | return -EINVAL; | ||
638 | } | ||
639 | dy = var->yoffset - info->var.yoffset; | ||
640 | |||
641 | if (dy) { | ||
642 | pr_debug("%s: Panning screen of %d lines\n", __func__, dy); | ||
643 | |||
644 | dmaaddr = fbdev->av1->start_addr; | ||
645 | dmaaddr += (info->fix.line_length * dy); | ||
646 | /* TODO: Wait for current frame to finished */ | ||
647 | |||
648 | fbdev->av1->start_addr = (unsigned long)dmaaddr; | ||
649 | fbdev->av2->start_addr = (unsigned long)dmaaddr + fbdev->line_len; | ||
650 | } | ||
651 | |||
652 | return 0; | ||
653 | |||
654 | } | ||
655 | |||
656 | /* 0 unblank, 1 blank, 2 no vsync, 3 no hsync, 4 off */ | ||
657 | static int bfin_adv7393_fb_blank(int blank, struct fb_info *info) | ||
658 | { | ||
659 | struct adv7393fb_device *fbdev = to_adv7393fb_device(info); | ||
660 | |||
661 | switch (blank) { | ||
662 | |||
663 | case VESA_NO_BLANKING: | ||
664 | /* Turn on panel */ | ||
665 | adv7393_mode(fbdev->client, BLANK_OFF); | ||
666 | break; | ||
667 | |||
668 | case VESA_VSYNC_SUSPEND: | ||
669 | case VESA_HSYNC_SUSPEND: | ||
670 | case VESA_POWERDOWN: | ||
671 | /* Turn off panel */ | ||
672 | adv7393_mode(fbdev->client, BLANK_ON); | ||
673 | break; | ||
674 | |||
675 | default: | ||
676 | return -EINVAL; | ||
677 | break; | ||
678 | } | ||
679 | return 0; | ||
680 | } | ||
681 | |||
682 | int bfin_adv7393_fb_cursor(struct fb_info *info, struct fb_cursor *cursor) | ||
683 | { | ||
684 | if (nocursor) | ||
685 | return 0; | ||
686 | else | ||
687 | return -EINVAL; /* just to force soft_cursor() call */ | ||
688 | } | ||
689 | |||
690 | static int bfin_adv7393_fb_setcolreg(u_int regno, u_int red, u_int green, | ||
691 | u_int blue, u_int transp, | ||
692 | struct fb_info *info) | ||
693 | { | ||
694 | if (regno >= BFIN_LCD_NBR_PALETTE_ENTRIES) | ||
695 | return -EINVAL; | ||
696 | |||
697 | if (info->var.grayscale) | ||
698 | /* grayscale = 0.30*R + 0.59*G + 0.11*B */ | ||
699 | red = green = blue = (red * 77 + green * 151 + blue * 28) >> 8; | ||
700 | |||
701 | if (info->fix.visual == FB_VISUAL_TRUECOLOR) { | ||
702 | u32 value; | ||
703 | /* Place color in the pseudopalette */ | ||
704 | if (regno > 16) | ||
705 | return -EINVAL; | ||
706 | |||
707 | red >>= (16 - info->var.red.length); | ||
708 | green >>= (16 - info->var.green.length); | ||
709 | blue >>= (16 - info->var.blue.length); | ||
710 | |||
711 | value = (red << info->var.red.offset) | | ||
712 | (green << info->var.green.offset)| | ||
713 | (blue << info->var.blue.offset); | ||
714 | value &= 0xFFFF; | ||
715 | |||
716 | ((u32 *) (info->pseudo_palette))[regno] = value; | ||
717 | } | ||
718 | |||
719 | return 0; | ||
720 | } | ||
721 | |||
722 | static int __devexit bfin_adv7393_fb_remove(struct i2c_client *client) | ||
723 | { | ||
724 | struct adv7393fb_device *fbdev = i2c_get_clientdata(client); | ||
725 | |||
726 | adv7393_mode(client, POWER_DOWN); | ||
727 | |||
728 | if (fbdev->fb_mem) | ||
729 | dma_free_coherent(NULL, fbdev->fb_len, fbdev->fb_mem, fbdev->dma_handle); | ||
730 | free_dma(CH_PPI); | ||
731 | free_irq(IRQ_PPI_ERROR, fbdev); | ||
732 | unregister_framebuffer(&fbdev->info); | ||
733 | remove_proc_entry("driver/adv7393", NULL); | ||
734 | fb_dealloc_cmap(&fbdev->info.cmap); | ||
735 | kfree(fbdev->info.pseudo_palette); | ||
736 | |||
737 | if (ANOMALY_05000400) | ||
738 | gpio_free(P_IDENT(P_PPI0_FS3)); /* FS3 */ | ||
739 | peripheral_free_list(ppi_pins); | ||
740 | kfree(fbdev); | ||
741 | |||
742 | return 0; | ||
743 | } | ||
744 | |||
745 | #ifdef CONFIG_PM | ||
746 | static int bfin_adv7393_fb_suspend(struct device *dev) | ||
747 | { | ||
748 | struct adv7393fb_device *fbdev = dev_get_drvdata(dev); | ||
749 | |||
750 | if (fbdev->open) { | ||
751 | bfin_disable_dma(); | ||
752 | bfin_disable_ppi(); | ||
753 | dma_desc_list(fbdev, DESTRUCT); | ||
754 | } | ||
755 | adv7393_mode(fbdev->client, POWER_DOWN); | ||
756 | |||
757 | return 0; | ||
758 | } | ||
759 | |||
760 | static int bfin_adv7393_fb_resume(struct device *dev) | ||
761 | { | ||
762 | struct adv7393fb_device *fbdev = dev_get_drvdata(dev); | ||
763 | |||
764 | adv7393_mode(fbdev->client, POWER_ON); | ||
765 | |||
766 | if (fbdev->open) { | ||
767 | dma_desc_list(fbdev, BUILD); | ||
768 | bfin_config_ppi(fbdev); | ||
769 | bfin_config_dma(fbdev); | ||
770 | bfin_enable_ppi(); | ||
771 | } | ||
772 | |||
773 | return 0; | ||
774 | } | ||
775 | |||
776 | static const struct dev_pm_ops bfin_adv7393_dev_pm_ops = { | ||
777 | .suspend = bfin_adv7393_fb_suspend, | ||
778 | .resume = bfin_adv7393_fb_resume, | ||
779 | }; | ||
780 | #endif | ||
781 | |||
782 | static const struct i2c_device_id bfin_adv7393_id[] = { | ||
783 | {DRIVER_NAME, 0}, | ||
784 | {} | ||
785 | }; | ||
786 | |||
787 | MODULE_DEVICE_TABLE(i2c, bfin_adv7393_id); | ||
788 | |||
789 | static struct i2c_driver bfin_adv7393_fb_driver = { | ||
790 | .driver = { | ||
791 | .name = DRIVER_NAME, | ||
792 | #ifdef CONFIG_PM | ||
793 | .pm = &bfin_adv7393_dev_pm_ops, | ||
794 | #endif | ||
795 | }, | ||
796 | .probe = bfin_adv7393_fb_probe, | ||
797 | .remove = __devexit_p(bfin_adv7393_fb_remove), | ||
798 | .id_table = bfin_adv7393_id, | ||
799 | }; | ||
800 | |||
801 | static int __init bfin_adv7393_fb_driver_init(void) | ||
802 | { | ||
803 | #if defined(CONFIG_I2C_BLACKFIN_TWI) || defined(CONFIG_I2C_BLACKFIN_TWI_MODULE) | ||
804 | request_module("i2c-bfin-twi"); | ||
805 | #else | ||
806 | request_module("i2c-gpio"); | ||
807 | #endif | ||
808 | |||
809 | return i2c_add_driver(&bfin_adv7393_fb_driver); | ||
810 | } | ||
811 | module_init(bfin_adv7393_fb_driver_init); | ||
812 | |||
813 | static void __exit bfin_adv7393_fb_driver_cleanup(void) | ||
814 | { | ||
815 | i2c_del_driver(&bfin_adv7393_fb_driver); | ||
816 | } | ||
817 | module_exit(bfin_adv7393_fb_driver_cleanup); | ||
818 | |||
819 | MODULE_LICENSE("GPL"); | ||
820 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | ||
821 | MODULE_DESCRIPTION("Frame buffer driver for ADV7393/2 Video Encoder"); | ||
822 | |||
823 | module_param(mode, int, 0); | ||
824 | MODULE_PARM_DESC(mode, | ||
825 | "Video Mode (0=NTSC,1=PAL,2=NTSC 640x480,3=PAL 640x480,4=NTSC YCbCr input,5=PAL YCbCr input)"); | ||
826 | |||
827 | module_param(mem, int, 0); | ||
828 | MODULE_PARM_DESC(mem, | ||
829 | "Size of frame buffer memory 1=Single 2=Double Size (allows y-panning / frame stacking)"); | ||
830 | |||
831 | module_param(nocursor, int, 0644); | ||
832 | MODULE_PARM_DESC(nocursor, "cursor enable/disable"); | ||
diff --git a/drivers/video/bfin_adv7393fb.h b/drivers/video/bfin_adv7393fb.h new file mode 100644 index 000000000000..8c7f9e4fc6eb --- /dev/null +++ b/drivers/video/bfin_adv7393fb.h | |||
@@ -0,0 +1,321 @@ | |||
1 | /* | ||
2 | * Frame buffer driver for ADV7393/2 video encoder | ||
3 | * | ||
4 | * Copyright 2006-2009 Analog Devices Inc. | ||
5 | * Licensed under the GPL-2 or late. | ||
6 | */ | ||
7 | |||
8 | #ifndef __BFIN_ADV7393FB_H__ | ||
9 | #define __BFIN_ADV7393FB_H__ | ||
10 | |||
11 | #define BFIN_LCD_NBR_PALETTE_ENTRIES 256 | ||
12 | |||
13 | #ifdef CONFIG_NTSC | ||
14 | # define VMODE 0 | ||
15 | #endif | ||
16 | #ifdef CONFIG_PAL | ||
17 | # define VMODE 1 | ||
18 | #endif | ||
19 | #ifdef CONFIG_NTSC_640x480 | ||
20 | # define VMODE 2 | ||
21 | #endif | ||
22 | #ifdef CONFIG_PAL_640x480 | ||
23 | # define VMODE 3 | ||
24 | #endif | ||
25 | #ifdef CONFIG_NTSC_YCBCR | ||
26 | # define VMODE 4 | ||
27 | #endif | ||
28 | #ifdef CONFIG_PAL_YCBCR | ||
29 | # define VMODE 5 | ||
30 | #endif | ||
31 | |||
32 | #ifndef VMODE | ||
33 | # define VMODE 1 | ||
34 | #endif | ||
35 | |||
36 | #ifdef CONFIG_ADV7393_2XMEM | ||
37 | # define VMEM 2 | ||
38 | #else | ||
39 | # define VMEM 1 | ||
40 | #endif | ||
41 | |||
42 | #if defined(CONFIG_BF537) || defined(CONFIG_BF536) || defined(CONFIG_BF534) | ||
43 | # define DMA_CFG_VAL 0x7935 /* Set Sync Bit */ | ||
44 | # define VB_DUMMY_MEMORY_SOURCE L1_DATA_B_START | ||
45 | #else | ||
46 | # define DMA_CFG_VAL 0x7915 | ||
47 | # define VB_DUMMY_MEMORY_SOURCE BOOT_ROM_START | ||
48 | #endif | ||
49 | |||
50 | enum { | ||
51 | DESTRUCT, | ||
52 | BUILD, | ||
53 | }; | ||
54 | |||
55 | enum { | ||
56 | POWER_ON, | ||
57 | POWER_DOWN, | ||
58 | BLANK_ON, | ||
59 | BLANK_OFF, | ||
60 | }; | ||
61 | |||
62 | #define DRIVER_NAME "bfin-adv7393" | ||
63 | |||
64 | struct adv7393fb_modes { | ||
65 | const s8 name[25]; /* Full name */ | ||
66 | u16 xres; /* Active Horizonzal Pixels */ | ||
67 | u16 yres; /* Active Vertical Pixels */ | ||
68 | u16 bpp; | ||
69 | u16 vmode; | ||
70 | u16 a_lines; /* Active Lines per Field */ | ||
71 | u16 vb1_lines; /* Vertical Blanking Field 1 Lines */ | ||
72 | u16 vb2_lines; /* Vertical Blanking Field 2 Lines */ | ||
73 | u16 tot_lines; /* Total Lines per Frame */ | ||
74 | u16 boeft_blank; /* Before Odd/Even Field Transition No. of Blank Pixels */ | ||
75 | u16 aoeft_blank; /* After Odd/Even Field Transition No. of Blank Pixels */ | ||
76 | const s8 *adv7393_i2c_initd; | ||
77 | u16 adv7393_i2c_initd_len; | ||
78 | }; | ||
79 | |||
80 | static const u8 init_NTSC_TESTPATTERN[] = { | ||
81 | 0x00, 0x1E, /* Power up all DACs and PLL */ | ||
82 | 0x01, 0x00, /* SD-Only Mode */ | ||
83 | 0x80, 0x10, /* SSAF Luma Filter Enabled, NTSC Mode */ | ||
84 | 0x82, 0xCB, /* Step control on, pixel data valid, pedestal on, PrPb SSAF on, CVBS/YC output */ | ||
85 | 0x84, 0x40, /* SD Color Bar Test Pattern Enabled, DAC 2 = Luma, DAC 3 = Chroma */ | ||
86 | }; | ||
87 | |||
88 | static const u8 init_NTSC[] = { | ||
89 | 0x00, 0x1E, /* Power up all DACs and PLL */ | ||
90 | 0xC3, 0x26, /* Program RGB->YCrCb Color Space convertion matrix */ | ||
91 | 0xC5, 0x12, /* Program RGB->YCrCb Color Space convertion matrix */ | ||
92 | 0xC2, 0x4A, /* Program RGB->YCrCb Color Space convertion matrix */ | ||
93 | 0xC6, 0x5E, /* Program RGB->YCrCb Color Space convertion matrix */ | ||
94 | 0xBD, 0x19, /* Program RGB->YCrCb Color Space convertion matrix */ | ||
95 | 0xBF, 0x42, /* Program RGB->YCrCb Color Space convertion matrix */ | ||
96 | 0x8C, 0x1F, /* NTSC Subcarrier Frequency */ | ||
97 | 0x8D, 0x7C, /* NTSC Subcarrier Frequency */ | ||
98 | 0x8E, 0xF0, /* NTSC Subcarrier Frequency */ | ||
99 | 0x8F, 0x21, /* NTSC Subcarrier Frequency */ | ||
100 | 0x01, 0x00, /* SD-Only Mode */ | ||
101 | 0x80, 0x30, /* SSAF Luma Filter Enabled, NTSC Mode */ | ||
102 | 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */ | ||
103 | 0x87, 0x80, /* SD Color Bar Test Pattern Enabled, DAC 2 = Luma, DAC 3 = Chroma */ | ||
104 | 0x86, 0x82, | ||
105 | 0x8B, 0x11, | ||
106 | 0x88, 0x20, | ||
107 | 0x8A, 0x0d, | ||
108 | }; | ||
109 | |||
110 | static const u8 init_PAL[] = { | ||
111 | 0x00, 0x1E, /* Power up all DACs and PLL */ | ||
112 | 0xC3, 0x26, /* Program RGB->YCrCb Color Space convertion matrix */ | ||
113 | 0xC5, 0x12, /* Program RGB->YCrCb Color Space convertion matrix */ | ||
114 | 0xC2, 0x4A, /* Program RGB->YCrCb Color Space convertion matrix */ | ||
115 | 0xC6, 0x5E, /* Program RGB->YCrCb Color Space convertion matrix */ | ||
116 | 0xBD, 0x19, /* Program RGB->YCrCb Color Space convertion matrix */ | ||
117 | 0xBF, 0x42, /* Program RGB->YCrCb Color Space convertion matrix */ | ||
118 | 0x8C, 0xCB, /* PAL Subcarrier Frequency */ | ||
119 | 0x8D, 0x8A, /* PAL Subcarrier Frequency */ | ||
120 | 0x8E, 0x09, /* PAL Subcarrier Frequency */ | ||
121 | 0x8F, 0x2A, /* PAL Subcarrier Frequency */ | ||
122 | 0x01, 0x00, /* SD-Only Mode */ | ||
123 | 0x80, 0x11, /* SSAF Luma Filter Enabled, PAL Mode */ | ||
124 | 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */ | ||
125 | 0x87, 0x80, /* SD Color Bar Test Pattern Enabled, DAC 2 = Luma, DAC 3 = Chroma */ | ||
126 | 0x86, 0x82, | ||
127 | 0x8B, 0x11, | ||
128 | 0x88, 0x20, | ||
129 | 0x8A, 0x0d, | ||
130 | }; | ||
131 | |||
132 | static const u8 init_NTSC_YCbCr[] = { | ||
133 | 0x00, 0x1E, /* Power up all DACs and PLL */ | ||
134 | 0x8C, 0x1F, /* NTSC Subcarrier Frequency */ | ||
135 | 0x8D, 0x7C, /* NTSC Subcarrier Frequency */ | ||
136 | 0x8E, 0xF0, /* NTSC Subcarrier Frequency */ | ||
137 | 0x8F, 0x21, /* NTSC Subcarrier Frequency */ | ||
138 | 0x01, 0x00, /* SD-Only Mode */ | ||
139 | 0x80, 0x30, /* SSAF Luma Filter Enabled, NTSC Mode */ | ||
140 | 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */ | ||
141 | 0x87, 0x00, /* DAC 2 = Luma, DAC 3 = Chroma */ | ||
142 | 0x86, 0x82, | ||
143 | 0x8B, 0x11, | ||
144 | 0x88, 0x08, | ||
145 | 0x8A, 0x0d, | ||
146 | }; | ||
147 | |||
148 | static const u8 init_PAL_YCbCr[] = { | ||
149 | 0x00, 0x1E, /* Power up all DACs and PLL */ | ||
150 | 0x8C, 0xCB, /* PAL Subcarrier Frequency */ | ||
151 | 0x8D, 0x8A, /* PAL Subcarrier Frequency */ | ||
152 | 0x8E, 0x09, /* PAL Subcarrier Frequency */ | ||
153 | 0x8F, 0x2A, /* PAL Subcarrier Frequency */ | ||
154 | 0x01, 0x00, /* SD-Only Mode */ | ||
155 | 0x80, 0x11, /* SSAF Luma Filter Enabled, PAL Mode */ | ||
156 | 0x82, 0x8B, /* Step control on, pixel data invalid, pedestal on, PrPb SSAF on, CVBS/YC output */ | ||
157 | 0x87, 0x00, /* DAC 2 = Luma, DAC 3 = Chroma */ | ||
158 | 0x86, 0x82, | ||
159 | 0x8B, 0x11, | ||
160 | 0x88, 0x08, | ||
161 | 0x8A, 0x0d, | ||
162 | }; | ||
163 | |||
164 | static struct adv7393fb_modes known_modes[] = { | ||
165 | /* NTSC 720x480 CRT */ | ||
166 | { | ||
167 | .name = "NTSC 720x480", | ||
168 | .xres = 720, | ||
169 | .yres = 480, | ||
170 | .bpp = 16, | ||
171 | .vmode = FB_VMODE_INTERLACED, | ||
172 | .a_lines = 240, | ||
173 | .vb1_lines = 22, | ||
174 | .vb2_lines = 23, | ||
175 | .tot_lines = 525, | ||
176 | .boeft_blank = 16, | ||
177 | .aoeft_blank = 122, | ||
178 | .adv7393_i2c_initd = init_NTSC, | ||
179 | .adv7393_i2c_initd_len = sizeof(init_NTSC) | ||
180 | }, | ||
181 | /* PAL 720x480 CRT */ | ||
182 | { | ||
183 | .name = "PAL 720x576", | ||
184 | .xres = 720, | ||
185 | .yres = 576, | ||
186 | .bpp = 16, | ||
187 | .vmode = FB_VMODE_INTERLACED, | ||
188 | .a_lines = 288, | ||
189 | .vb1_lines = 24, | ||
190 | .vb2_lines = 25, | ||
191 | .tot_lines = 625, | ||
192 | .boeft_blank = 12, | ||
193 | .aoeft_blank = 132, | ||
194 | .adv7393_i2c_initd = init_PAL, | ||
195 | .adv7393_i2c_initd_len = sizeof(init_PAL) | ||
196 | }, | ||
197 | /* NTSC 640x480 CRT Experimental */ | ||
198 | { | ||
199 | .name = "NTSC 640x480", | ||
200 | .xres = 640, | ||
201 | .yres = 480, | ||
202 | .bpp = 16, | ||
203 | .vmode = FB_VMODE_INTERLACED, | ||
204 | .a_lines = 240, | ||
205 | .vb1_lines = 22, | ||
206 | .vb2_lines = 23, | ||
207 | .tot_lines = 525, | ||
208 | .boeft_blank = 16 + 40, | ||
209 | .aoeft_blank = 122 + 40, | ||
210 | .adv7393_i2c_initd = init_NTSC, | ||
211 | .adv7393_i2c_initd_len = sizeof(init_NTSC) | ||
212 | }, | ||
213 | /* PAL 640x480 CRT Experimental */ | ||
214 | { | ||
215 | .name = "PAL 640x480", | ||
216 | .xres = 640, | ||
217 | .yres = 480, | ||
218 | .bpp = 16, | ||
219 | .vmode = FB_VMODE_INTERLACED, | ||
220 | .a_lines = 288 - 20, | ||
221 | .vb1_lines = 24 + 20, | ||
222 | .vb2_lines = 25 + 20, | ||
223 | .tot_lines = 625, | ||
224 | .boeft_blank = 12 + 40, | ||
225 | .aoeft_blank = 132 + 40, | ||
226 | .adv7393_i2c_initd = init_PAL, | ||
227 | .adv7393_i2c_initd_len = sizeof(init_PAL) | ||
228 | }, | ||
229 | /* NTSC 720x480 YCbCR */ | ||
230 | { | ||
231 | .name = "NTSC 720x480 YCbCR", | ||
232 | .xres = 720, | ||
233 | .yres = 480, | ||
234 | .bpp = 16, | ||
235 | .vmode = FB_VMODE_INTERLACED, | ||
236 | .a_lines = 240, | ||
237 | .vb1_lines = 22, | ||
238 | .vb2_lines = 23, | ||
239 | .tot_lines = 525, | ||
240 | .boeft_blank = 16, | ||
241 | .aoeft_blank = 122, | ||
242 | .adv7393_i2c_initd = init_NTSC_YCbCr, | ||
243 | .adv7393_i2c_initd_len = sizeof(init_NTSC_YCbCr) | ||
244 | }, | ||
245 | /* PAL 720x480 CRT */ | ||
246 | { | ||
247 | .name = "PAL 720x576 YCbCR", | ||
248 | .xres = 720, | ||
249 | .yres = 576, | ||
250 | .bpp = 16, | ||
251 | .vmode = FB_VMODE_INTERLACED, | ||
252 | .a_lines = 288, | ||
253 | .vb1_lines = 24, | ||
254 | .vb2_lines = 25, | ||
255 | .tot_lines = 625, | ||
256 | .boeft_blank = 12, | ||
257 | .aoeft_blank = 132, | ||
258 | .adv7393_i2c_initd = init_PAL_YCbCr, | ||
259 | .adv7393_i2c_initd_len = sizeof(init_PAL_YCbCr) | ||
260 | } | ||
261 | }; | ||
262 | |||
263 | struct adv7393fb_regs { | ||
264 | |||
265 | }; | ||
266 | |||
267 | struct adv7393fb_device { | ||
268 | struct fb_info info; /* FB driver info record */ | ||
269 | |||
270 | struct i2c_client *client; | ||
271 | |||
272 | struct dmasg *descriptor_list_head; | ||
273 | struct dmasg *vb1; | ||
274 | struct dmasg *av1; | ||
275 | struct dmasg *vb2; | ||
276 | struct dmasg *av2; | ||
277 | |||
278 | dma_addr_t dma_handle; | ||
279 | |||
280 | struct fb_info bfin_adv7393_fb; | ||
281 | |||
282 | struct adv7393fb_modes *modes; | ||
283 | |||
284 | struct adv7393fb_regs *regs; /* Registers memory map */ | ||
285 | size_t regs_len; | ||
286 | size_t fb_len; | ||
287 | size_t line_len; | ||
288 | u16 open; | ||
289 | u16 *fb_mem; /* RGB Buffer */ | ||
290 | |||
291 | }; | ||
292 | |||
293 | #define to_adv7393fb_device(_info) \ | ||
294 | (_info ? container_of(_info, struct adv7393fb_device, info) : NULL); | ||
295 | |||
296 | static int bfin_adv7393_fb_open(struct fb_info *info, int user); | ||
297 | static int bfin_adv7393_fb_release(struct fb_info *info, int user); | ||
298 | static int bfin_adv7393_fb_check_var(struct fb_var_screeninfo *var, | ||
299 | struct fb_info *info); | ||
300 | |||
301 | static int bfin_adv7393_fb_pan_display(struct fb_var_screeninfo *var, | ||
302 | struct fb_info *info); | ||
303 | |||
304 | static int bfin_adv7393_fb_blank(int blank, struct fb_info *info); | ||
305 | |||
306 | static void bfin_config_ppi(struct adv7393fb_device *fbdev); | ||
307 | static int bfin_config_dma(struct adv7393fb_device *fbdev); | ||
308 | static void bfin_disable_dma(void); | ||
309 | static void bfin_enable_ppi(void); | ||
310 | static void bfin_disable_ppi(void); | ||
311 | |||
312 | static inline int adv7393_write(struct i2c_client *client, u8 reg, u8 value); | ||
313 | static inline int adv7393_read(struct i2c_client *client, u8 reg); | ||
314 | static int adv7393_write_block(struct i2c_client *client, const u8 *data, | ||
315 | unsigned int len); | ||
316 | |||
317 | int bfin_adv7393_fb_cursor(struct fb_info *info, struct fb_cursor *cursor); | ||
318 | static int bfin_adv7393_fb_setcolreg(u_int, u_int, u_int, u_int, | ||
319 | u_int, struct fb_info *info); | ||
320 | |||
321 | #endif | ||
diff --git a/drivers/video/fbmon.c b/drivers/video/fbmon.c index 563a98b88e9b..4f57485f8c54 100644 --- a/drivers/video/fbmon.c +++ b/drivers/video/fbmon.c | |||
@@ -973,6 +973,90 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) | |||
973 | DPRINTK("========================================\n"); | 973 | DPRINTK("========================================\n"); |
974 | } | 974 | } |
975 | 975 | ||
976 | /** | ||
977 | * fb_edid_add_monspecs() - add monitor video modes from E-EDID data | ||
978 | * @edid: 128 byte array with an E-EDID block | ||
979 | * @spacs: monitor specs to be extended | ||
980 | */ | ||
981 | void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) | ||
982 | { | ||
983 | unsigned char *block; | ||
984 | struct fb_videomode *m; | ||
985 | int num = 0, i; | ||
986 | u8 svd[64], edt[(128 - 4) / DETAILED_TIMING_DESCRIPTION_SIZE]; | ||
987 | u8 pos = 4, svd_n = 0; | ||
988 | |||
989 | if (!edid) | ||
990 | return; | ||
991 | |||
992 | if (!edid_checksum(edid)) | ||
993 | return; | ||
994 | |||
995 | if (edid[0] != 0x2 || | ||
996 | edid[2] < 4 || edid[2] > 128 - DETAILED_TIMING_DESCRIPTION_SIZE) | ||
997 | return; | ||
998 | |||
999 | DPRINTK(" Short Video Descriptors\n"); | ||
1000 | |||
1001 | while (pos < edid[2]) { | ||
1002 | u8 len = edid[pos] & 0x1f, type = (edid[pos] >> 5) & 7; | ||
1003 | pr_debug("Data block %u of %u bytes\n", type, len); | ||
1004 | if (type == 2) | ||
1005 | for (i = pos; i < pos + len; i++) { | ||
1006 | u8 idx = edid[pos + i] & 0x7f; | ||
1007 | svd[svd_n++] = idx; | ||
1008 | pr_debug("N%sative mode #%d\n", | ||
1009 | edid[pos + i] & 0x80 ? "" : "on-n", idx); | ||
1010 | } | ||
1011 | pos += len + 1; | ||
1012 | } | ||
1013 | |||
1014 | block = edid + edid[2]; | ||
1015 | |||
1016 | DPRINTK(" Extended Detailed Timings\n"); | ||
1017 | |||
1018 | for (i = 0; i < (128 - edid[2]) / DETAILED_TIMING_DESCRIPTION_SIZE; | ||
1019 | i++, block += DETAILED_TIMING_DESCRIPTION_SIZE) | ||
1020 | if (PIXEL_CLOCK) | ||
1021 | edt[num++] = block - edid; | ||
1022 | |||
1023 | /* Yikes, EDID data is totally useless */ | ||
1024 | if (!(num + svd_n)) | ||
1025 | return; | ||
1026 | |||
1027 | m = kzalloc((specs->modedb_len + num + svd_n) * | ||
1028 | sizeof(struct fb_videomode), GFP_KERNEL); | ||
1029 | |||
1030 | if (!m) | ||
1031 | return; | ||
1032 | |||
1033 | memcpy(m, specs->modedb, specs->modedb_len * sizeof(struct fb_videomode)); | ||
1034 | |||
1035 | for (i = specs->modedb_len; i < specs->modedb_len + num; i++) { | ||
1036 | get_detailed_timing(edid + edt[i - specs->modedb_len], &m[i]); | ||
1037 | if (i == specs->modedb_len) | ||
1038 | m[i].flag |= FB_MODE_IS_FIRST; | ||
1039 | pr_debug("Adding %ux%u@%u\n", m[i].xres, m[i].yres, m[i].refresh); | ||
1040 | } | ||
1041 | |||
1042 | for (i = specs->modedb_len + num; i < specs->modedb_len + num + svd_n; i++) { | ||
1043 | int idx = svd[i - specs->modedb_len - num]; | ||
1044 | if (!idx || idx > 63) { | ||
1045 | pr_warning("Reserved SVD code %d\n", idx); | ||
1046 | } else if (idx > ARRAY_SIZE(cea_modes) || !cea_modes[idx].xres) { | ||
1047 | pr_warning("Unimplemented SVD code %d\n", idx); | ||
1048 | } else { | ||
1049 | memcpy(&m[i], cea_modes + idx, sizeof(m[i])); | ||
1050 | pr_debug("Adding SVD #%d: %ux%u@%u\n", idx, | ||
1051 | m[i].xres, m[i].yres, m[i].refresh); | ||
1052 | } | ||
1053 | } | ||
1054 | |||
1055 | kfree(specs->modedb); | ||
1056 | specs->modedb = m; | ||
1057 | specs->modedb_len = specs->modedb_len + num + svd_n; | ||
1058 | } | ||
1059 | |||
976 | /* | 1060 | /* |
977 | * VESA Generalized Timing Formula (GTF) | 1061 | * VESA Generalized Timing Formula (GTF) |
978 | */ | 1062 | */ |
@@ -1289,6 +1373,9 @@ void fb_edid_to_monspecs(unsigned char *edid, struct fb_monspecs *specs) | |||
1289 | { | 1373 | { |
1290 | specs = NULL; | 1374 | specs = NULL; |
1291 | } | 1375 | } |
1376 | void fb_edid_add_monspecs(unsigned char *edid, struct fb_monspecs *specs) | ||
1377 | { | ||
1378 | } | ||
1292 | void fb_destroy_modedb(struct fb_videomode *modedb) | 1379 | void fb_destroy_modedb(struct fb_videomode *modedb) |
1293 | { | 1380 | { |
1294 | } | 1381 | } |
@@ -1396,6 +1483,7 @@ EXPORT_SYMBOL(fb_firmware_edid); | |||
1396 | 1483 | ||
1397 | EXPORT_SYMBOL(fb_parse_edid); | 1484 | EXPORT_SYMBOL(fb_parse_edid); |
1398 | EXPORT_SYMBOL(fb_edid_to_monspecs); | 1485 | EXPORT_SYMBOL(fb_edid_to_monspecs); |
1486 | EXPORT_SYMBOL(fb_edid_add_monspecs); | ||
1399 | EXPORT_SYMBOL(fb_get_mode); | 1487 | EXPORT_SYMBOL(fb_get_mode); |
1400 | EXPORT_SYMBOL(fb_validate_mode); | 1488 | EXPORT_SYMBOL(fb_validate_mode); |
1401 | EXPORT_SYMBOL(fb_destroy_modedb); | 1489 | EXPORT_SYMBOL(fb_destroy_modedb); |
diff --git a/drivers/video/modedb.c b/drivers/video/modedb.c index de450c1fb869..7a61ba6a4850 100644 --- a/drivers/video/modedb.c +++ b/drivers/video/modedb.c | |||
@@ -278,6 +278,53 @@ static const struct fb_videomode modedb[] = { | |||
278 | }; | 278 | }; |
279 | 279 | ||
280 | #ifdef CONFIG_FB_MODE_HELPERS | 280 | #ifdef CONFIG_FB_MODE_HELPERS |
281 | const struct fb_videomode cea_modes[64] = { | ||
282 | /* #1: 640x480p@59.94/60Hz */ | ||
283 | [1] = { | ||
284 | NULL, 60, 640, 480, 39722, 48, 16, 33, 10, 96, 2, 0, FB_VMODE_NONINTERLACED, 0, | ||
285 | }, | ||
286 | /* #3: 720x480p@59.94/60Hz */ | ||
287 | [3] = { | ||
288 | NULL, 60, 720, 480, 37037, 60, 16, 30, 9, 62, 6, 0, FB_VMODE_NONINTERLACED, 0, | ||
289 | }, | ||
290 | /* #5: 1920x1080i@59.94/60Hz */ | ||
291 | [5] = { | ||
292 | NULL, 60, 1920, 1080, 13763, 148, 88, 15, 2, 44, 5, | ||
293 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_INTERLACED, 0, | ||
294 | }, | ||
295 | /* #7: 720(1440)x480iH@59.94/60Hz */ | ||
296 | [7] = { | ||
297 | NULL, 60, 1440, 480, 18554/*37108*/, 114, 38, 15, 4, 124, 3, 0, FB_VMODE_INTERLACED, 0, | ||
298 | }, | ||
299 | /* #9: 720(1440)x240pH@59.94/60Hz */ | ||
300 | [9] = { | ||
301 | NULL, 60, 1440, 240, 18554, 114, 38, 16, 4, 124, 3, 0, FB_VMODE_NONINTERLACED, 0, | ||
302 | }, | ||
303 | /* #18: 720x576pH@50Hz */ | ||
304 | [18] = { | ||
305 | NULL, 50, 720, 576, 37037, 68, 12, 39, 5, 64, 5, 0, FB_VMODE_NONINTERLACED, 0, | ||
306 | }, | ||
307 | /* #19: 1280x720p@50Hz */ | ||
308 | [19] = { | ||
309 | NULL, 50, 1280, 720, 13468, 220, 440, 20, 5, 40, 5, | ||
310 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0, | ||
311 | }, | ||
312 | /* #20: 1920x1080i@50Hz */ | ||
313 | [20] = { | ||
314 | NULL, 50, 1920, 1080, 13480, 148, 528, 15, 5, 528, 5, | ||
315 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_INTERLACED, 0, | ||
316 | }, | ||
317 | /* #32: 1920x1080p@23.98/24Hz */ | ||
318 | [32] = { | ||
319 | NULL, 24, 1920, 1080, 13468, 148, 638, 36, 4, 44, 5, | ||
320 | FB_SYNC_HOR_HIGH_ACT | FB_SYNC_VERT_HIGH_ACT, FB_VMODE_NONINTERLACED, 0, | ||
321 | }, | ||
322 | /* #35: (2880)x480p4x@59.94/60Hz */ | ||
323 | [35] = { | ||
324 | NULL, 60, 2880, 480, 9250, 240, 64, 30, 9, 248, 6, 0, FB_VMODE_NONINTERLACED, 0, | ||
325 | }, | ||
326 | }; | ||
327 | |||
281 | const struct fb_videomode vesa_modes[] = { | 328 | const struct fb_videomode vesa_modes[] = { |
282 | /* 0 640x350-85 VESA */ | 329 | /* 0 640x350-85 VESA */ |
283 | { NULL, 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3, | 330 | { NULL, 85, 640, 350, 31746, 96, 32, 60, 32, 64, 3, |
diff --git a/drivers/video/mx3fb.c b/drivers/video/mx3fb.c index ca0f6be9d12e..cb013919e9ce 100644 --- a/drivers/video/mx3fb.c +++ b/drivers/video/mx3fb.c | |||
@@ -1474,8 +1474,7 @@ static int mx3fb_probe(struct platform_device *pdev) | |||
1474 | goto eremap; | 1474 | goto eremap; |
1475 | } | 1475 | } |
1476 | 1476 | ||
1477 | pr_debug("Remapped %x to %x at %p\n", sdc_reg->start, sdc_reg->end, | 1477 | pr_debug("Remapped %pR at %p\n", sdc_reg, mx3fb->reg_base); |
1478 | mx3fb->reg_base); | ||
1479 | 1478 | ||
1480 | /* IDMAC interface */ | 1479 | /* IDMAC interface */ |
1481 | dmaengine_get(); | 1480 | dmaengine_get(); |
diff --git a/drivers/video/s1d13xxxfb.c b/drivers/video/s1d13xxxfb.c index a6247fc081ab..28b1c6c3d8ac 100644 --- a/drivers/video/s1d13xxxfb.c +++ b/drivers/video/s1d13xxxfb.c | |||
@@ -410,28 +410,6 @@ s1d13xxxfb_pan_display(struct fb_var_screeninfo *var, struct fb_info *info) | |||
410 | ************************************************************/ | 410 | ************************************************************/ |
411 | 411 | ||
412 | /** | 412 | /** |
413 | * bltbit_wait_bitset - waits for change in register value | ||
414 | * @info : framebuffer structure | ||
415 | * @bit : value expected in register | ||
416 | * @timeout : ... | ||
417 | * | ||
418 | * waits until value changes INTO bit | ||
419 | */ | ||
420 | static u8 | ||
421 | bltbit_wait_bitset(struct fb_info *info, u8 bit, int timeout) | ||
422 | { | ||
423 | while (!(s1d13xxxfb_readreg(info->par, S1DREG_BBLT_CTL0) & bit)) { | ||
424 | udelay(10); | ||
425 | if (!--timeout) { | ||
426 | dbg_blit("wait_bitset timeout\n"); | ||
427 | break; | ||
428 | } | ||
429 | } | ||
430 | |||
431 | return timeout; | ||
432 | } | ||
433 | |||
434 | /** | ||
435 | * bltbit_wait_bitclear - waits for change in register value | 413 | * bltbit_wait_bitclear - waits for change in register value |
436 | * @info : frambuffer structure | 414 | * @info : frambuffer structure |
437 | * @bit : value currently in register | 415 | * @bit : value currently in register |
@@ -454,34 +432,6 @@ bltbit_wait_bitclear(struct fb_info *info, u8 bit, int timeout) | |||
454 | return timeout; | 432 | return timeout; |
455 | } | 433 | } |
456 | 434 | ||
457 | /** | ||
458 | * bltbit_fifo_status - checks the current status of the fifo | ||
459 | * @info : framebuffer structure | ||
460 | * | ||
461 | * returns number of free words in buffer | ||
462 | */ | ||
463 | static u8 | ||
464 | bltbit_fifo_status(struct fb_info *info) | ||
465 | { | ||
466 | u8 status; | ||
467 | |||
468 | status = s1d13xxxfb_readreg(info->par, S1DREG_BBLT_CTL0); | ||
469 | |||
470 | /* its empty so room for 16 words */ | ||
471 | if (status & BBLT_FIFO_EMPTY) | ||
472 | return 16; | ||
473 | |||
474 | /* its full so we dont want to add */ | ||
475 | if (status & BBLT_FIFO_FULL) | ||
476 | return 0; | ||
477 | |||
478 | /* its atleast half full but we can add one atleast */ | ||
479 | if (status & BBLT_FIFO_NOT_FULL) | ||
480 | return 1; | ||
481 | |||
482 | return 0; | ||
483 | } | ||
484 | |||
485 | /* | 435 | /* |
486 | * s1d13xxxfb_bitblt_copyarea - accelerated copyarea function | 436 | * s1d13xxxfb_bitblt_copyarea - accelerated copyarea function |
487 | * @info : framebuffer structure | 437 | * @info : framebuffer structure |
diff --git a/drivers/video/s3c-fb.c b/drivers/video/s3c-fb.c index f9aca9d13d1b..83ce9a04d872 100644 --- a/drivers/video/s3c-fb.c +++ b/drivers/video/s3c-fb.c | |||
@@ -23,6 +23,7 @@ | |||
23 | #include <linux/io.h> | 23 | #include <linux/io.h> |
24 | #include <linux/uaccess.h> | 24 | #include <linux/uaccess.h> |
25 | #include <linux/interrupt.h> | 25 | #include <linux/interrupt.h> |
26 | #include <linux/pm_runtime.h> | ||
26 | 27 | ||
27 | #include <mach/map.h> | 28 | #include <mach/map.h> |
28 | #include <plat/regs-fb-v4.h> | 29 | #include <plat/regs-fb-v4.h> |
@@ -1013,8 +1014,30 @@ static int s3c_fb_ioctl(struct fb_info *info, unsigned int cmd, | |||
1013 | return ret; | 1014 | return ret; |
1014 | } | 1015 | } |
1015 | 1016 | ||
1017 | static int s3c_fb_open(struct fb_info *info, int user) | ||
1018 | { | ||
1019 | struct s3c_fb_win *win = info->par; | ||
1020 | struct s3c_fb *sfb = win->parent; | ||
1021 | |||
1022 | pm_runtime_get_sync(sfb->dev); | ||
1023 | |||
1024 | return 0; | ||
1025 | } | ||
1026 | |||
1027 | static int s3c_fb_release(struct fb_info *info, int user) | ||
1028 | { | ||
1029 | struct s3c_fb_win *win = info->par; | ||
1030 | struct s3c_fb *sfb = win->parent; | ||
1031 | |||
1032 | pm_runtime_put_sync(sfb->dev); | ||
1033 | |||
1034 | return 0; | ||
1035 | } | ||
1036 | |||
1016 | static struct fb_ops s3c_fb_ops = { | 1037 | static struct fb_ops s3c_fb_ops = { |
1017 | .owner = THIS_MODULE, | 1038 | .owner = THIS_MODULE, |
1039 | .fb_open = s3c_fb_open, | ||
1040 | .fb_release = s3c_fb_release, | ||
1018 | .fb_check_var = s3c_fb_check_var, | 1041 | .fb_check_var = s3c_fb_check_var, |
1019 | .fb_set_par = s3c_fb_set_par, | 1042 | .fb_set_par = s3c_fb_set_par, |
1020 | .fb_blank = s3c_fb_blank, | 1043 | .fb_blank = s3c_fb_blank, |
@@ -1322,6 +1345,8 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) | |||
1322 | 1345 | ||
1323 | clk_enable(sfb->bus_clk); | 1346 | clk_enable(sfb->bus_clk); |
1324 | 1347 | ||
1348 | pm_runtime_enable(sfb->dev); | ||
1349 | |||
1325 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 1350 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
1326 | if (!res) { | 1351 | if (!res) { |
1327 | dev_err(dev, "failed to find registers\n"); | 1352 | dev_err(dev, "failed to find registers\n"); |
@@ -1360,6 +1385,9 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) | |||
1360 | 1385 | ||
1361 | dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); | 1386 | dev_dbg(dev, "got resources (regs %p), probing windows\n", sfb->regs); |
1362 | 1387 | ||
1388 | platform_set_drvdata(pdev, sfb); | ||
1389 | pm_runtime_get_sync(sfb->dev); | ||
1390 | |||
1363 | /* setup gpio and output polarity controls */ | 1391 | /* setup gpio and output polarity controls */ |
1364 | 1392 | ||
1365 | pd->setup_gpio(); | 1393 | pd->setup_gpio(); |
@@ -1400,6 +1428,7 @@ static int __devinit s3c_fb_probe(struct platform_device *pdev) | |||
1400 | } | 1428 | } |
1401 | 1429 | ||
1402 | platform_set_drvdata(pdev, sfb); | 1430 | platform_set_drvdata(pdev, sfb); |
1431 | pm_runtime_put_sync(sfb->dev); | ||
1403 | 1432 | ||
1404 | return 0; | 1433 | return 0; |
1405 | 1434 | ||
@@ -1434,6 +1463,8 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) | |||
1434 | struct s3c_fb *sfb = platform_get_drvdata(pdev); | 1463 | struct s3c_fb *sfb = platform_get_drvdata(pdev); |
1435 | int win; | 1464 | int win; |
1436 | 1465 | ||
1466 | pm_runtime_get_sync(sfb->dev); | ||
1467 | |||
1437 | for (win = 0; win < S3C_FB_MAX_WIN; win++) | 1468 | for (win = 0; win < S3C_FB_MAX_WIN; win++) |
1438 | if (sfb->windows[win]) | 1469 | if (sfb->windows[win]) |
1439 | s3c_fb_release_win(sfb, sfb->windows[win]); | 1470 | s3c_fb_release_win(sfb, sfb->windows[win]); |
@@ -1450,12 +1481,74 @@ static int __devexit s3c_fb_remove(struct platform_device *pdev) | |||
1450 | 1481 | ||
1451 | kfree(sfb); | 1482 | kfree(sfb); |
1452 | 1483 | ||
1484 | pm_runtime_put_sync(sfb->dev); | ||
1485 | pm_runtime_disable(sfb->dev); | ||
1486 | |||
1453 | return 0; | 1487 | return 0; |
1454 | } | 1488 | } |
1455 | 1489 | ||
1456 | #ifdef CONFIG_PM | 1490 | #ifdef CONFIG_PM |
1457 | static int s3c_fb_suspend(struct platform_device *pdev, pm_message_t state) | 1491 | static int s3c_fb_suspend(struct device *dev) |
1492 | { | ||
1493 | struct platform_device *pdev = to_platform_device(dev); | ||
1494 | struct s3c_fb *sfb = platform_get_drvdata(pdev); | ||
1495 | struct s3c_fb_win *win; | ||
1496 | int win_no; | ||
1497 | |||
1498 | for (win_no = S3C_FB_MAX_WIN - 1; win_no >= 0; win_no--) { | ||
1499 | win = sfb->windows[win_no]; | ||
1500 | if (!win) | ||
1501 | continue; | ||
1502 | |||
1503 | /* use the blank function to push into power-down */ | ||
1504 | s3c_fb_blank(FB_BLANK_POWERDOWN, win->fbinfo); | ||
1505 | } | ||
1506 | |||
1507 | clk_disable(sfb->bus_clk); | ||
1508 | return 0; | ||
1509 | } | ||
1510 | |||
1511 | static int s3c_fb_resume(struct device *dev) | ||
1512 | { | ||
1513 | struct platform_device *pdev = to_platform_device(dev); | ||
1514 | struct s3c_fb *sfb = platform_get_drvdata(pdev); | ||
1515 | struct s3c_fb_platdata *pd = sfb->pdata; | ||
1516 | struct s3c_fb_win *win; | ||
1517 | int win_no; | ||
1518 | |||
1519 | clk_enable(sfb->bus_clk); | ||
1520 | |||
1521 | /* setup registers */ | ||
1522 | writel(pd->vidcon1, sfb->regs + VIDCON1); | ||
1523 | |||
1524 | /* zero all windows before we do anything */ | ||
1525 | for (win_no = 0; win_no < sfb->variant.nr_windows; win_no++) | ||
1526 | s3c_fb_clear_win(sfb, win_no); | ||
1527 | |||
1528 | for (win_no = 0; win_no < sfb->variant.nr_windows - 1; win_no++) { | ||
1529 | void __iomem *regs = sfb->regs + sfb->variant.keycon; | ||
1530 | |||
1531 | regs += (win_no * 8); | ||
1532 | writel(0xffffff, regs + WKEYCON0); | ||
1533 | writel(0xffffff, regs + WKEYCON1); | ||
1534 | } | ||
1535 | |||
1536 | /* restore framebuffers */ | ||
1537 | for (win_no = 0; win_no < S3C_FB_MAX_WIN; win_no++) { | ||
1538 | win = sfb->windows[win_no]; | ||
1539 | if (!win) | ||
1540 | continue; | ||
1541 | |||
1542 | dev_dbg(&pdev->dev, "resuming window %d\n", win_no); | ||
1543 | s3c_fb_set_par(win->fbinfo); | ||
1544 | } | ||
1545 | |||
1546 | return 0; | ||
1547 | } | ||
1548 | |||
1549 | int s3c_fb_runtime_suspend(struct device *dev) | ||
1458 | { | 1550 | { |
1551 | struct platform_device *pdev = to_platform_device(dev); | ||
1459 | struct s3c_fb *sfb = platform_get_drvdata(pdev); | 1552 | struct s3c_fb *sfb = platform_get_drvdata(pdev); |
1460 | struct s3c_fb_win *win; | 1553 | struct s3c_fb_win *win; |
1461 | int win_no; | 1554 | int win_no; |
@@ -1473,8 +1566,9 @@ static int s3c_fb_suspend(struct platform_device *pdev, pm_message_t state) | |||
1473 | return 0; | 1566 | return 0; |
1474 | } | 1567 | } |
1475 | 1568 | ||
1476 | static int s3c_fb_resume(struct platform_device *pdev) | 1569 | int s3c_fb_runtime_resume(struct device *dev) |
1477 | { | 1570 | { |
1571 | struct platform_device *pdev = to_platform_device(dev); | ||
1478 | struct s3c_fb *sfb = platform_get_drvdata(pdev); | 1572 | struct s3c_fb *sfb = platform_get_drvdata(pdev); |
1479 | struct s3c_fb_platdata *pd = sfb->pdata; | 1573 | struct s3c_fb_platdata *pd = sfb->pdata; |
1480 | struct s3c_fb_win *win; | 1574 | struct s3c_fb_win *win; |
@@ -1509,9 +1603,12 @@ static int s3c_fb_resume(struct platform_device *pdev) | |||
1509 | 1603 | ||
1510 | return 0; | 1604 | return 0; |
1511 | } | 1605 | } |
1606 | |||
1512 | #else | 1607 | #else |
1513 | #define s3c_fb_suspend NULL | 1608 | #define s3c_fb_suspend NULL |
1514 | #define s3c_fb_resume NULL | 1609 | #define s3c_fb_resume NULL |
1610 | #define s3c_fb_runtime_suspend NULL | ||
1611 | #define s3c_fb_runtime_resume NULL | ||
1515 | #endif | 1612 | #endif |
1516 | 1613 | ||
1517 | 1614 | ||
@@ -1710,15 +1807,21 @@ static struct platform_device_id s3c_fb_driver_ids[] = { | |||
1710 | }; | 1807 | }; |
1711 | MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids); | 1808 | MODULE_DEVICE_TABLE(platform, s3c_fb_driver_ids); |
1712 | 1809 | ||
1810 | static const struct dev_pm_ops s3cfb_pm_ops = { | ||
1811 | .suspend = s3c_fb_suspend, | ||
1812 | .resume = s3c_fb_resume, | ||
1813 | .runtime_suspend = s3c_fb_runtime_suspend, | ||
1814 | .runtime_resume = s3c_fb_runtime_resume, | ||
1815 | }; | ||
1816 | |||
1713 | static struct platform_driver s3c_fb_driver = { | 1817 | static struct platform_driver s3c_fb_driver = { |
1714 | .probe = s3c_fb_probe, | 1818 | .probe = s3c_fb_probe, |
1715 | .remove = __devexit_p(s3c_fb_remove), | 1819 | .remove = __devexit_p(s3c_fb_remove), |
1716 | .suspend = s3c_fb_suspend, | ||
1717 | .resume = s3c_fb_resume, | ||
1718 | .id_table = s3c_fb_driver_ids, | 1820 | .id_table = s3c_fb_driver_ids, |
1719 | .driver = { | 1821 | .driver = { |
1720 | .name = "s3c-fb", | 1822 | .name = "s3c-fb", |
1721 | .owner = THIS_MODULE, | 1823 | .owner = THIS_MODULE, |
1824 | .pm = &s3cfb_pm_ops, | ||
1722 | }, | 1825 | }, |
1723 | }; | 1826 | }; |
1724 | 1827 | ||
diff --git a/drivers/video/sh_mipi_dsi.c b/drivers/video/sh_mipi_dsi.c index 3f3d431033ca..b40dc423cbdf 100644 --- a/drivers/video/sh_mipi_dsi.c +++ b/drivers/video/sh_mipi_dsi.c | |||
@@ -21,18 +21,38 @@ | |||
21 | #include <video/sh_mipi_dsi.h> | 21 | #include <video/sh_mipi_dsi.h> |
22 | #include <video/sh_mobile_lcdc.h> | 22 | #include <video/sh_mobile_lcdc.h> |
23 | 23 | ||
24 | #define CMTSRTCTR 0x80d0 | 24 | #define SYSCTRL 0x0000 |
25 | #define CMTSRTREQ 0x8070 | 25 | #define SYSCONF 0x0004 |
26 | 26 | #define TIMSET 0x0008 | |
27 | #define RESREQSET0 0x0018 | ||
28 | #define RESREQSET1 0x001c | ||
29 | #define HSTTOVSET 0x0020 | ||
30 | #define LPRTOVSET 0x0024 | ||
31 | #define TATOVSET 0x0028 | ||
32 | #define PRTOVSET 0x002c | ||
33 | #define DSICTRL 0x0030 | ||
27 | #define DSIINTE 0x0060 | 34 | #define DSIINTE 0x0060 |
35 | #define PHYCTRL 0x0070 | ||
36 | |||
37 | /* relative to linkbase */ | ||
38 | #define DTCTR 0x0000 | ||
39 | #define VMCTR1 0x0020 | ||
40 | #define VMCTR2 0x0024 | ||
41 | #define VMLEN1 0x0028 | ||
42 | #define CMTSRTREQ 0x0070 | ||
43 | #define CMTSRTCTR 0x00d0 | ||
28 | 44 | ||
29 | /* E.g., sh7372 has 2 MIPI-DSIs - one for each LCDC */ | 45 | /* E.g., sh7372 has 2 MIPI-DSIs - one for each LCDC */ |
30 | #define MAX_SH_MIPI_DSI 2 | 46 | #define MAX_SH_MIPI_DSI 2 |
31 | 47 | ||
32 | struct sh_mipi { | 48 | struct sh_mipi { |
33 | void __iomem *base; | 49 | void __iomem *base; |
50 | void __iomem *linkbase; | ||
34 | struct clk *dsit_clk; | 51 | struct clk *dsit_clk; |
35 | struct clk *dsip_clk; | 52 | struct clk *dsip_clk; |
53 | void *next_board_data; | ||
54 | void (*next_display_on)(void *board_data, struct fb_info *info); | ||
55 | void (*next_display_off)(void *board_data); | ||
36 | }; | 56 | }; |
37 | 57 | ||
38 | static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI]; | 58 | static struct sh_mipi *mipi_dsi[MAX_SH_MIPI_DSI]; |
@@ -55,10 +75,10 @@ static int sh_mipi_send_short(struct sh_mipi *mipi, u8 dsi_cmd, | |||
55 | int cnt = 100; | 75 | int cnt = 100; |
56 | 76 | ||
57 | /* transmit a short packet to LCD panel */ | 77 | /* transmit a short packet to LCD panel */ |
58 | iowrite32(1 | data, mipi->base + 0x80d0); /* CMTSRTCTR */ | 78 | iowrite32(1 | data, mipi->linkbase + CMTSRTCTR); |
59 | iowrite32(1, mipi->base + 0x8070); /* CMTSRTREQ */ | 79 | iowrite32(1, mipi->linkbase + CMTSRTREQ); |
60 | 80 | ||
61 | while ((ioread32(mipi->base + 0x8070) & 1) && --cnt) | 81 | while ((ioread32(mipi->linkbase + CMTSRTREQ) & 1) && --cnt) |
62 | udelay(1); | 82 | udelay(1); |
63 | 83 | ||
64 | return cnt ? 0 : -ETIMEDOUT; | 84 | return cnt ? 0 : -ETIMEDOUT; |
@@ -90,7 +110,7 @@ static void sh_mipi_dsi_enable(struct sh_mipi *mipi, bool enable) | |||
90 | * enable LCDC data tx, transition to LPS after completion of each HS | 110 | * enable LCDC data tx, transition to LPS after completion of each HS |
91 | * packet | 111 | * packet |
92 | */ | 112 | */ |
93 | iowrite32(0x00000002 | enable, mipi->base + 0x8000); /* DTCTR */ | 113 | iowrite32(0x00000002 | enable, mipi->linkbase + DTCTR); |
94 | } | 114 | } |
95 | 115 | ||
96 | static void sh_mipi_shutdown(struct platform_device *pdev) | 116 | static void sh_mipi_shutdown(struct platform_device *pdev) |
@@ -105,12 +125,18 @@ static void mipi_display_on(void *arg, struct fb_info *info) | |||
105 | struct sh_mipi *mipi = arg; | 125 | struct sh_mipi *mipi = arg; |
106 | 126 | ||
107 | sh_mipi_dsi_enable(mipi, true); | 127 | sh_mipi_dsi_enable(mipi, true); |
128 | |||
129 | if (mipi->next_display_on) | ||
130 | mipi->next_display_on(mipi->next_board_data, info); | ||
108 | } | 131 | } |
109 | 132 | ||
110 | static void mipi_display_off(void *arg) | 133 | static void mipi_display_off(void *arg) |
111 | { | 134 | { |
112 | struct sh_mipi *mipi = arg; | 135 | struct sh_mipi *mipi = arg; |
113 | 136 | ||
137 | if (mipi->next_display_off) | ||
138 | mipi->next_display_off(mipi->next_board_data); | ||
139 | |||
114 | sh_mipi_dsi_enable(mipi, false); | 140 | sh_mipi_dsi_enable(mipi, false); |
115 | } | 141 | } |
116 | 142 | ||
@@ -223,10 +249,10 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, | |||
223 | return -EINVAL; | 249 | return -EINVAL; |
224 | 250 | ||
225 | /* reset DSI link */ | 251 | /* reset DSI link */ |
226 | iowrite32(0x00000001, base); /* SYSCTRL */ | 252 | iowrite32(0x00000001, base + SYSCTRL); |
227 | /* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */ | 253 | /* Hold reset for 100 cycles of the slowest of bus, HS byte and LP clock */ |
228 | udelay(50); | 254 | udelay(50); |
229 | iowrite32(0x00000000, base); /* SYSCTRL */ | 255 | iowrite32(0x00000000, base + SYSCTRL); |
230 | 256 | ||
231 | /* setup DSI link */ | 257 | /* setup DSI link */ |
232 | 258 | ||
@@ -238,7 +264,7 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, | |||
238 | * ECC check enable | 264 | * ECC check enable |
239 | * additionally enable first two lanes | 265 | * additionally enable first two lanes |
240 | */ | 266 | */ |
241 | iowrite32(0x00003703, base + 0x04); /* SYSCONF */ | 267 | iowrite32(0x00003703, base + SYSCONF); |
242 | /* | 268 | /* |
243 | * T_wakeup = 0x7000 | 269 | * T_wakeup = 0x7000 |
244 | * T_hs-trail = 3 | 270 | * T_hs-trail = 3 |
@@ -246,28 +272,28 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, | |||
246 | * T_clk-trail = 3 | 272 | * T_clk-trail = 3 |
247 | * T_clk-prepare = 2 | 273 | * T_clk-prepare = 2 |
248 | */ | 274 | */ |
249 | iowrite32(0x70003332, base + 0x08); /* TIMSET */ | 275 | iowrite32(0x70003332, base + TIMSET); |
250 | /* no responses requested */ | 276 | /* no responses requested */ |
251 | iowrite32(0x00000000, base + 0x18); /* RESREQSET0 */ | 277 | iowrite32(0x00000000, base + RESREQSET0); |
252 | /* request response to packets of type 0x28 */ | 278 | /* request response to packets of type 0x28 */ |
253 | iowrite32(0x00000100, base + 0x1c); /* RESREQSET1 */ | 279 | iowrite32(0x00000100, base + RESREQSET1); |
254 | /* High-speed transmission timeout, default 0xffffffff */ | 280 | /* High-speed transmission timeout, default 0xffffffff */ |
255 | iowrite32(0x0fffffff, base + 0x20); /* HSTTOVSET */ | 281 | iowrite32(0x0fffffff, base + HSTTOVSET); |
256 | /* LP reception timeout, default 0xffffffff */ | 282 | /* LP reception timeout, default 0xffffffff */ |
257 | iowrite32(0x0fffffff, base + 0x24); /* LPRTOVSET */ | 283 | iowrite32(0x0fffffff, base + LPRTOVSET); |
258 | /* Turn-around timeout, default 0xffffffff */ | 284 | /* Turn-around timeout, default 0xffffffff */ |
259 | iowrite32(0x0fffffff, base + 0x28); /* TATOVSET */ | 285 | iowrite32(0x0fffffff, base + TATOVSET); |
260 | /* Peripheral reset timeout, default 0xffffffff */ | 286 | /* Peripheral reset timeout, default 0xffffffff */ |
261 | iowrite32(0x0fffffff, base + 0x2c); /* PRTOVSET */ | 287 | iowrite32(0x0fffffff, base + PRTOVSET); |
262 | /* Enable timeout counters */ | 288 | /* Enable timeout counters */ |
263 | iowrite32(0x00000f00, base + 0x30); /* DSICTRL */ | 289 | iowrite32(0x00000f00, base + DSICTRL); |
264 | /* Interrupts not used, disable all */ | 290 | /* Interrupts not used, disable all */ |
265 | iowrite32(0, base + DSIINTE); | 291 | iowrite32(0, base + DSIINTE); |
266 | /* DSI-Tx bias on */ | 292 | /* DSI-Tx bias on */ |
267 | iowrite32(0x00000001, base + 0x70); /* PHYCTRL */ | 293 | iowrite32(0x00000001, base + PHYCTRL); |
268 | udelay(200); | 294 | udelay(200); |
269 | /* Deassert resets, power on, set multiplier */ | 295 | /* Deassert resets, power on, set multiplier */ |
270 | iowrite32(0x03070b01, base + 0x70); /* PHYCTRL */ | 296 | iowrite32(0x03070b01, base + PHYCTRL); |
271 | 297 | ||
272 | /* setup l-bridge */ | 298 | /* setup l-bridge */ |
273 | 299 | ||
@@ -275,20 +301,21 @@ static int __init sh_mipi_setup(struct sh_mipi *mipi, | |||
275 | * Enable transmission of all packets, | 301 | * Enable transmission of all packets, |
276 | * transmit LPS after each HS packet completion | 302 | * transmit LPS after each HS packet completion |
277 | */ | 303 | */ |
278 | iowrite32(0x00000006, base + 0x8000); /* DTCTR */ | 304 | iowrite32(0x00000006, mipi->linkbase + DTCTR); |
279 | /* VSYNC width = 2 (<< 17) */ | 305 | /* VSYNC width = 2 (<< 17) */ |
280 | iowrite32(0x00040000 | (pctype << 12) | datatype, base + 0x8020); /* VMCTR1 */ | 306 | iowrite32(0x00040000 | (pctype << 12) | datatype, |
307 | mipi->linkbase + VMCTR1); | ||
281 | /* | 308 | /* |
282 | * Non-burst mode with sync pulses: VSE and HSE are output, | 309 | * Non-burst mode with sync pulses: VSE and HSE are output, |
283 | * HSA period allowed, no commands in LP | 310 | * HSA period allowed, no commands in LP |
284 | */ | 311 | */ |
285 | iowrite32(0x00e00000, base + 0x8024); /* VMCTR2 */ | 312 | iowrite32(0x00e00000, mipi->linkbase + VMCTR2); |
286 | /* | 313 | /* |
287 | * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see | 314 | * 0x660 = 1632 bytes per line (RGB24, 544 pixels: see |
288 | * sh_mobile_lcdc_info.ch[0].lcd_cfg[0].xres), HSALEN = 1 - default | 315 | * sh_mobile_lcdc_info.ch[0].lcd_cfg[0].xres), HSALEN = 1 - default |
289 | * (unused, since VMCTR2[HSABM] = 0) | 316 | * (unused, since VMCTR2[HSABM] = 0) |
290 | */ | 317 | */ |
291 | iowrite32(1 | (linelength << 16), base + 0x8028); /* VMLEN1 */ | 318 | iowrite32(1 | (linelength << 16), mipi->linkbase + VMLEN1); |
292 | 319 | ||
293 | msleep(5); | 320 | msleep(5); |
294 | 321 | ||
@@ -321,11 +348,12 @@ static int __init sh_mipi_probe(struct platform_device *pdev) | |||
321 | struct sh_mipi *mipi; | 348 | struct sh_mipi *mipi; |
322 | struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; | 349 | struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; |
323 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 350 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
351 | struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
324 | unsigned long rate, f_current; | 352 | unsigned long rate, f_current; |
325 | int idx = pdev->id, ret; | 353 | int idx = pdev->id, ret; |
326 | char dsip_clk[] = "dsi.p_clk"; | 354 | char dsip_clk[] = "dsi.p_clk"; |
327 | 355 | ||
328 | if (!res || idx >= ARRAY_SIZE(mipi_dsi) || !pdata) | 356 | if (!res || !res2 || idx >= ARRAY_SIZE(mipi_dsi) || !pdata) |
329 | return -ENODEV; | 357 | return -ENODEV; |
330 | 358 | ||
331 | mutex_lock(&array_lock); | 359 | mutex_lock(&array_lock); |
@@ -356,6 +384,18 @@ static int __init sh_mipi_probe(struct platform_device *pdev) | |||
356 | goto emap; | 384 | goto emap; |
357 | } | 385 | } |
358 | 386 | ||
387 | if (!request_mem_region(res2->start, resource_size(res2), pdev->name)) { | ||
388 | dev_err(&pdev->dev, "MIPI register region 2 already claimed\n"); | ||
389 | ret = -EBUSY; | ||
390 | goto ereqreg2; | ||
391 | } | ||
392 | |||
393 | mipi->linkbase = ioremap(res2->start, resource_size(res2)); | ||
394 | if (!mipi->linkbase) { | ||
395 | ret = -ENOMEM; | ||
396 | goto emap2; | ||
397 | } | ||
398 | |||
359 | mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk"); | 399 | mipi->dsit_clk = clk_get(&pdev->dev, "dsit_clk"); |
360 | if (IS_ERR(mipi->dsit_clk)) { | 400 | if (IS_ERR(mipi->dsit_clk)) { |
361 | ret = PTR_ERR(mipi->dsit_clk); | 401 | ret = PTR_ERR(mipi->dsit_clk); |
@@ -412,6 +452,11 @@ static int __init sh_mipi_probe(struct platform_device *pdev) | |||
412 | mutex_unlock(&array_lock); | 452 | mutex_unlock(&array_lock); |
413 | platform_set_drvdata(pdev, mipi); | 453 | platform_set_drvdata(pdev, mipi); |
414 | 454 | ||
455 | /* Save original LCDC callbacks */ | ||
456 | mipi->next_board_data = pdata->lcd_chan->board_cfg.board_data; | ||
457 | mipi->next_display_on = pdata->lcd_chan->board_cfg.display_on; | ||
458 | mipi->next_display_off = pdata->lcd_chan->board_cfg.display_off; | ||
459 | |||
415 | /* Set up LCDC callbacks */ | 460 | /* Set up LCDC callbacks */ |
416 | pdata->lcd_chan->board_cfg.board_data = mipi; | 461 | pdata->lcd_chan->board_cfg.board_data = mipi; |
417 | pdata->lcd_chan->board_cfg.display_on = mipi_display_on; | 462 | pdata->lcd_chan->board_cfg.display_on = mipi_display_on; |
@@ -431,6 +476,10 @@ eclkpget: | |||
431 | esettrate: | 476 | esettrate: |
432 | clk_put(mipi->dsit_clk); | 477 | clk_put(mipi->dsit_clk); |
433 | eclktget: | 478 | eclktget: |
479 | iounmap(mipi->linkbase); | ||
480 | emap2: | ||
481 | release_mem_region(res2->start, resource_size(res2)); | ||
482 | ereqreg2: | ||
434 | iounmap(mipi->base); | 483 | iounmap(mipi->base); |
435 | emap: | 484 | emap: |
436 | release_mem_region(res->start, resource_size(res)); | 485 | release_mem_region(res->start, resource_size(res)); |
@@ -447,6 +496,7 @@ static int __exit sh_mipi_remove(struct platform_device *pdev) | |||
447 | { | 496 | { |
448 | struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; | 497 | struct sh_mipi_dsi_info *pdata = pdev->dev.platform_data; |
449 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | 498 | struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); |
499 | struct resource *res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1); | ||
450 | struct sh_mipi *mipi = platform_get_drvdata(pdev); | 500 | struct sh_mipi *mipi = platform_get_drvdata(pdev); |
451 | int i, ret; | 501 | int i, ret; |
452 | 502 | ||
@@ -475,6 +525,9 @@ static int __exit sh_mipi_remove(struct platform_device *pdev) | |||
475 | clk_disable(mipi->dsit_clk); | 525 | clk_disable(mipi->dsit_clk); |
476 | clk_put(mipi->dsit_clk); | 526 | clk_put(mipi->dsit_clk); |
477 | clk_put(mipi->dsip_clk); | 527 | clk_put(mipi->dsip_clk); |
528 | iounmap(mipi->linkbase); | ||
529 | if (res2) | ||
530 | release_mem_region(res2->start, resource_size(res2)); | ||
478 | iounmap(mipi->base); | 531 | iounmap(mipi->base); |
479 | if (res) | 532 | if (res) |
480 | release_mem_region(res->start, resource_size(res)); | 533 | release_mem_region(res->start, resource_size(res)); |
diff --git a/drivers/video/sh_mobile_hdmi.c b/drivers/video/sh_mobile_hdmi.c index d7df10315d8d..76f9fac9020f 100644 --- a/drivers/video/sh_mobile_hdmi.c +++ b/drivers/video/sh_mobile_hdmi.c | |||
@@ -209,7 +209,11 @@ enum hotplug_state { | |||
209 | struct sh_hdmi { | 209 | struct sh_hdmi { |
210 | void __iomem *base; | 210 | void __iomem *base; |
211 | enum hotplug_state hp_state; /* hot-plug status */ | 211 | enum hotplug_state hp_state; /* hot-plug status */ |
212 | bool preprogrammed_mode; /* use a pre-programmed VIC or the external mode */ | 212 | u8 preprogrammed_vic; /* use a pre-programmed VIC or |
213 | the external mode */ | ||
214 | u8 edid_block_addr; | ||
215 | u8 edid_segment_nr; | ||
216 | u8 edid_blocks; | ||
213 | struct clk *hdmi_clk; | 217 | struct clk *hdmi_clk; |
214 | struct device *dev; | 218 | struct device *dev; |
215 | struct fb_info *info; | 219 | struct fb_info *info; |
@@ -342,7 +346,7 @@ static void sh_hdmi_external_video_param(struct sh_hdmi *hdmi) | |||
342 | hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); | 346 | hdmi_write(hdmi, var->vsync_len, HDMI_EXTERNAL_V_DURATION); |
343 | 347 | ||
344 | /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */ | 348 | /* Set bit 0 of HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS here for external mode */ |
345 | if (!hdmi->preprogrammed_mode) | 349 | if (!hdmi->preprogrammed_vic) |
346 | hdmi_write(hdmi, sync | 1 | (voffset << 4), | 350 | hdmi_write(hdmi, sync | 1 | (voffset << 4), |
347 | HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); | 351 | HDMI_EXTERNAL_VIDEO_PARAM_SETTINGS); |
348 | } | 352 | } |
@@ -466,7 +470,18 @@ static void sh_hdmi_audio_config(struct sh_hdmi *hdmi) | |||
466 | */ | 470 | */ |
467 | static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) | 471 | static void sh_hdmi_phy_config(struct sh_hdmi *hdmi) |
468 | { | 472 | { |
469 | if (hdmi->var.yres > 480) { | 473 | if (hdmi->var.pixclock < 10000) { |
474 | /* for 1080p8bit 148MHz */ | ||
475 | hdmi_write(hdmi, 0x1d, HDMI_SLIPHDMIT_PARAM_SETTINGS_1); | ||
476 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_2); | ||
477 | hdmi_write(hdmi, 0x00, HDMI_SLIPHDMIT_PARAM_SETTINGS_3); | ||
478 | hdmi_write(hdmi, 0x4c, HDMI_SLIPHDMIT_PARAM_SETTINGS_5); | ||
479 | hdmi_write(hdmi, 0x1e, HDMI_SLIPHDMIT_PARAM_SETTINGS_6); | ||
480 | hdmi_write(hdmi, 0x48, HDMI_SLIPHDMIT_PARAM_SETTINGS_7); | ||
481 | hdmi_write(hdmi, 0x0e, HDMI_SLIPHDMIT_PARAM_SETTINGS_8); | ||
482 | hdmi_write(hdmi, 0x25, HDMI_SLIPHDMIT_PARAM_SETTINGS_9); | ||
483 | hdmi_write(hdmi, 0x04, HDMI_SLIPHDMIT_PARAM_SETTINGS_10); | ||
484 | } else if (hdmi->var.pixclock < 30000) { | ||
470 | /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ | 485 | /* 720p, 8bit, 74.25MHz. Might need to be adjusted for other formats */ |
471 | /* | 486 | /* |
472 | * [1:0] Speed_A | 487 | * [1:0] Speed_A |
@@ -565,13 +580,11 @@ static void sh_hdmi_avi_infoframe_setup(struct sh_hdmi *hdmi) | |||
565 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3); | 580 | hdmi_write(hdmi, 0x00, HDMI_CTRL_PKT_BUF_ACCESS_PB3); |
566 | 581 | ||
567 | /* | 582 | /* |
568 | * VIC = 1280 x 720p: ignored if external config is used | 583 | * VIC should be ignored if external config is used, so, we could just use 0, |
569 | * Send 2 for 720 x 480p, 16 for 1080p, ignored in external mode | 584 | * but play safe and use a valid value in any case just in case |
570 | */ | 585 | */ |
571 | if (hdmi->var.yres == 1080 && hdmi->var.xres == 1920) | 586 | if (hdmi->preprogrammed_vic) |
572 | vic = 16; | 587 | vic = hdmi->preprogrammed_vic; |
573 | else if (hdmi->var.yres == 480 && hdmi->var.xres == 720) | ||
574 | vic = 2; | ||
575 | else | 588 | else |
576 | vic = 4; | 589 | vic = 4; |
577 | hdmi_write(hdmi, vic, HDMI_CTRL_PKT_BUF_ACCESS_PB4); | 590 | hdmi_write(hdmi, vic, HDMI_CTRL_PKT_BUF_ACCESS_PB4); |
@@ -685,11 +698,21 @@ static void sh_hdmi_configure(struct sh_hdmi *hdmi) | |||
685 | } | 698 | } |
686 | 699 | ||
687 | static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, | 700 | static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, |
688 | const struct fb_videomode *mode) | 701 | const struct fb_videomode *mode, |
702 | unsigned long *hdmi_rate, unsigned long *parent_rate) | ||
689 | { | 703 | { |
690 | long target = PICOS2KHZ(mode->pixclock) * 1000, | 704 | unsigned long target = PICOS2KHZ(mode->pixclock) * 1000, rate_error; |
691 | rate = clk_round_rate(hdmi->hdmi_clk, target); | 705 | struct sh_mobile_hdmi_info *pdata = hdmi->dev->platform_data; |
692 | unsigned long rate_error = rate > 0 ? abs(rate - target) : ULONG_MAX; | 706 | |
707 | *hdmi_rate = clk_round_rate(hdmi->hdmi_clk, target); | ||
708 | if ((long)*hdmi_rate < 0) | ||
709 | *hdmi_rate = clk_get_rate(hdmi->hdmi_clk); | ||
710 | |||
711 | rate_error = (long)*hdmi_rate > 0 ? abs(*hdmi_rate - target) : ULONG_MAX; | ||
712 | if (rate_error && pdata->clk_optimize_parent) | ||
713 | rate_error = pdata->clk_optimize_parent(target, hdmi_rate, parent_rate); | ||
714 | else if (clk_get_parent(hdmi->hdmi_clk)) | ||
715 | *parent_rate = clk_get_rate(clk_get_parent(hdmi->hdmi_clk)); | ||
693 | 716 | ||
694 | dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n", | 717 | dev_dbg(hdmi->dev, "%u-%u-%u-%u x %u-%u-%u-%u\n", |
695 | mode->left_margin, mode->xres, | 718 | mode->left_margin, mode->xres, |
@@ -697,14 +720,15 @@ static unsigned long sh_hdmi_rate_error(struct sh_hdmi *hdmi, | |||
697 | mode->upper_margin, mode->yres, | 720 | mode->upper_margin, mode->yres, |
698 | mode->lower_margin, mode->vsync_len); | 721 | mode->lower_margin, mode->vsync_len); |
699 | 722 | ||
700 | dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz\n", target, | 723 | dev_dbg(hdmi->dev, "\t@%lu(+/-%lu)Hz, e=%lu / 1000, r=%uHz, p=%luHz\n", target, |
701 | rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0, | 724 | rate_error, rate_error ? 10000 / (10 * target / rate_error) : 0, |
702 | mode->refresh); | 725 | mode->refresh, *parent_rate); |
703 | 726 | ||
704 | return rate_error; | 727 | return rate_error; |
705 | } | 728 | } |
706 | 729 | ||
707 | static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) | 730 | static int sh_hdmi_read_edid(struct sh_hdmi *hdmi, unsigned long *hdmi_rate, |
731 | unsigned long *parent_rate) | ||
708 | { | 732 | { |
709 | struct fb_var_screeninfo tmpvar; | 733 | struct fb_var_screeninfo tmpvar; |
710 | struct fb_var_screeninfo *var = &tmpvar; | 734 | struct fb_var_screeninfo *var = &tmpvar; |
@@ -735,7 +759,38 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) | |||
735 | printk(KERN_CONT "\n"); | 759 | printk(KERN_CONT "\n"); |
736 | #endif | 760 | #endif |
737 | 761 | ||
738 | fb_edid_to_monspecs(edid, &hdmi->monspec); | 762 | if (!hdmi->edid_blocks) { |
763 | fb_edid_to_monspecs(edid, &hdmi->monspec); | ||
764 | hdmi->edid_blocks = edid[126] + 1; | ||
765 | |||
766 | dev_dbg(hdmi->dev, "%d main modes, %d extension blocks\n", | ||
767 | hdmi->monspec.modedb_len, hdmi->edid_blocks - 1); | ||
768 | } else { | ||
769 | dev_dbg(hdmi->dev, "Extension %u detected, DTD start %u\n", | ||
770 | edid[0], edid[2]); | ||
771 | fb_edid_add_monspecs(edid, &hdmi->monspec); | ||
772 | } | ||
773 | |||
774 | if (hdmi->edid_blocks > hdmi->edid_segment_nr * 2 + | ||
775 | (hdmi->edid_block_addr >> 7) + 1) { | ||
776 | /* More blocks to read */ | ||
777 | if (hdmi->edid_block_addr) { | ||
778 | hdmi->edid_block_addr = 0; | ||
779 | hdmi->edid_segment_nr++; | ||
780 | } else { | ||
781 | hdmi->edid_block_addr = 0x80; | ||
782 | } | ||
783 | /* Set EDID word address */ | ||
784 | hdmi_write(hdmi, hdmi->edid_block_addr, HDMI_EDID_WORD_ADDRESS); | ||
785 | /* Enable EDID interrupt */ | ||
786 | hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); | ||
787 | /* Set EDID segment pointer - starts reading EDID */ | ||
788 | hdmi_write(hdmi, hdmi->edid_segment_nr, HDMI_EDID_SEGMENT_POINTER); | ||
789 | return -EAGAIN; | ||
790 | } | ||
791 | |||
792 | /* All E-EDID blocks ready */ | ||
793 | dev_dbg(hdmi->dev, "%d main and extended modes\n", hdmi->monspec.modedb_len); | ||
739 | 794 | ||
740 | fb_get_options("sh_mobile_lcdc", &forced); | 795 | fb_get_options("sh_mobile_lcdc", &forced); |
741 | if (forced && *forced) { | 796 | if (forced && *forced) { |
@@ -754,11 +809,14 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) | |||
754 | for (i = 0, mode = hdmi->monspec.modedb; | 809 | for (i = 0, mode = hdmi->monspec.modedb; |
755 | f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match; | 810 | f_width && f_height && i < hdmi->monspec.modedb_len && !exact_match; |
756 | i++, mode++) { | 811 | i++, mode++) { |
757 | unsigned long rate_error = sh_hdmi_rate_error(hdmi, mode); | 812 | unsigned long rate_error; |
758 | 813 | ||
759 | /* No interest in unmatching modes */ | 814 | /* No interest in unmatching modes */ |
760 | if (f_width != mode->xres || f_height != mode->yres) | 815 | if (f_width != mode->xres || f_height != mode->yres) |
761 | continue; | 816 | continue; |
817 | |||
818 | rate_error = sh_hdmi_rate_error(hdmi, mode, hdmi_rate, parent_rate); | ||
819 | |||
762 | if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) | 820 | if (f_refresh == mode->refresh || (!f_refresh && !rate_error)) |
763 | /* | 821 | /* |
764 | * Exact match if either the refresh rate matches or it | 822 | * Exact match if either the refresh rate matches or it |
@@ -802,7 +860,7 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) | |||
802 | 860 | ||
803 | if (modelist) { | 861 | if (modelist) { |
804 | found = &modelist->mode; | 862 | found = &modelist->mode; |
805 | found_rate_error = sh_hdmi_rate_error(hdmi, found); | 863 | found_rate_error = sh_hdmi_rate_error(hdmi, found, hdmi_rate, parent_rate); |
806 | } | 864 | } |
807 | } | 865 | } |
808 | 866 | ||
@@ -810,16 +868,27 @@ static int sh_hdmi_read_edid(struct sh_hdmi *hdmi) | |||
810 | if (!found) | 868 | if (!found) |
811 | return -ENXIO; | 869 | return -ENXIO; |
812 | 870 | ||
813 | dev_info(hdmi->dev, "Using %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", | 871 | if (found->xres == 640 && found->yres == 480 && found->refresh == 60) |
814 | modelist ? "default" : "EDID", found->xres, found->yres, | 872 | hdmi->preprogrammed_vic = 1; |
815 | found->refresh, PICOS2KHZ(found->pixclock) * 1000, found_rate_error); | 873 | else if (found->xres == 720 && found->yres == 480 && found->refresh == 60) |
816 | 874 | hdmi->preprogrammed_vic = 2; | |
817 | if ((found->xres == 720 && found->yres == 480) || | 875 | else if (found->xres == 720 && found->yres == 576 && found->refresh == 50) |
818 | (found->xres == 1280 && found->yres == 720) || | 876 | hdmi->preprogrammed_vic = 17; |
819 | (found->xres == 1920 && found->yres == 1080)) | 877 | else if (found->xres == 1280 && found->yres == 720 && found->refresh == 60) |
820 | hdmi->preprogrammed_mode = true; | 878 | hdmi->preprogrammed_vic = 4; |
879 | else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 24) | ||
880 | hdmi->preprogrammed_vic = 32; | ||
881 | else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 50) | ||
882 | hdmi->preprogrammed_vic = 31; | ||
883 | else if (found->xres == 1920 && found->yres == 1080 && found->refresh == 60) | ||
884 | hdmi->preprogrammed_vic = 16; | ||
821 | else | 885 | else |
822 | hdmi->preprogrammed_mode = false; | 886 | hdmi->preprogrammed_vic = 0; |
887 | |||
888 | dev_dbg(hdmi->dev, "Using %s %s mode %ux%u@%uHz (%luHz), clock error %luHz\n", | ||
889 | modelist ? "default" : "EDID", hdmi->preprogrammed_vic ? "VIC" : "external", | ||
890 | found->xres, found->yres, found->refresh, | ||
891 | PICOS2KHZ(found->pixclock) * 1000, found_rate_error); | ||
823 | 892 | ||
824 | fb_videomode_to_var(&hdmi->var, found); | 893 | fb_videomode_to_var(&hdmi->var, found); |
825 | sh_hdmi_external_video_param(hdmi); | 894 | sh_hdmi_external_video_param(hdmi); |
@@ -868,32 +937,34 @@ static irqreturn_t sh_hdmi_hotplug(int irq, void *dev_id) | |||
868 | /* Check, if hot plug & MSENS pin status are both high */ | 937 | /* Check, if hot plug & MSENS pin status are both high */ |
869 | if ((msens & 0xC0) == 0xC0) { | 938 | if ((msens & 0xC0) == 0xC0) { |
870 | /* Display plug in */ | 939 | /* Display plug in */ |
940 | hdmi->edid_segment_nr = 0; | ||
941 | hdmi->edid_block_addr = 0; | ||
942 | hdmi->edid_blocks = 0; | ||
871 | hdmi->hp_state = HDMI_HOTPLUG_CONNECTED; | 943 | hdmi->hp_state = HDMI_HOTPLUG_CONNECTED; |
872 | 944 | ||
873 | /* Set EDID word address */ | 945 | /* Set EDID word address */ |
874 | hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); | 946 | hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); |
875 | /* Set EDID segment pointer */ | ||
876 | hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); | ||
877 | /* Enable EDID interrupt */ | 947 | /* Enable EDID interrupt */ |
878 | hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); | 948 | hdmi_write(hdmi, 0xC6, HDMI_INTERRUPT_MASK_1); |
949 | /* Set EDID segment pointer - starts reading EDID */ | ||
950 | hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); | ||
879 | } else if (!(status1 & 0x80)) { | 951 | } else if (!(status1 & 0x80)) { |
880 | /* Display unplug, beware multiple interrupts */ | 952 | /* Display unplug, beware multiple interrupts */ |
881 | if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) | 953 | if (hdmi->hp_state != HDMI_HOTPLUG_DISCONNECTED) { |
954 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; | ||
882 | schedule_delayed_work(&hdmi->edid_work, 0); | 955 | schedule_delayed_work(&hdmi->edid_work, 0); |
883 | 956 | } | |
884 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; | ||
885 | /* display_off will switch back to mode_a */ | 957 | /* display_off will switch back to mode_a */ |
886 | } | 958 | } |
887 | } else if (status1 & 2) { | 959 | } else if (status1 & 2) { |
888 | /* EDID error interrupt: retry */ | 960 | /* EDID error interrupt: retry */ |
889 | /* Set EDID word address */ | 961 | /* Set EDID word address */ |
890 | hdmi_write(hdmi, 0x00, HDMI_EDID_WORD_ADDRESS); | 962 | hdmi_write(hdmi, hdmi->edid_block_addr, HDMI_EDID_WORD_ADDRESS); |
891 | /* Set EDID segment pointer */ | 963 | /* Set EDID segment pointer */ |
892 | hdmi_write(hdmi, 0x00, HDMI_EDID_SEGMENT_POINTER); | 964 | hdmi_write(hdmi, hdmi->edid_segment_nr, HDMI_EDID_SEGMENT_POINTER); |
893 | } else if (status1 & 4) { | 965 | } else if (status1 & 4) { |
894 | /* Disable EDID interrupt */ | 966 | /* Disable EDID interrupt */ |
895 | hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1); | 967 | hdmi_write(hdmi, 0xC0, HDMI_INTERRUPT_MASK_1); |
896 | hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; | ||
897 | schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10)); | 968 | schedule_delayed_work(&hdmi->edid_work, msecs_to_jiffies(10)); |
898 | } | 969 | } |
899 | 970 | ||
@@ -972,39 +1043,37 @@ static bool sh_hdmi_must_reconfigure(struct sh_hdmi *hdmi) | |||
972 | 1043 | ||
973 | /** | 1044 | /** |
974 | * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock | 1045 | * sh_hdmi_clk_configure() - set HDMI clock frequency and enable the clock |
975 | * @hdmi: driver context | 1046 | * @hdmi: driver context |
976 | * @pixclock: pixel clock period in picoseconds | 1047 | * @hdmi_rate: HDMI clock frequency in Hz |
977 | * return: configured positive rate if successful | 1048 | * @parent_rate: if != 0 - set parent clock rate for optimal precision |
978 | * 0 if couldn't set the rate, but managed to enable the clock | 1049 | * return: configured positive rate if successful |
979 | * negative error, if couldn't enable the clock | 1050 | * 0 if couldn't set the rate, but managed to enable the |
1051 | * clock, negative error, if couldn't enable the clock | ||
980 | */ | 1052 | */ |
981 | static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long pixclock) | 1053 | static long sh_hdmi_clk_configure(struct sh_hdmi *hdmi, unsigned long hdmi_rate, |
1054 | unsigned long parent_rate) | ||
982 | { | 1055 | { |
983 | long rate; | ||
984 | int ret; | 1056 | int ret; |
985 | 1057 | ||
986 | rate = PICOS2KHZ(pixclock) * 1000; | 1058 | if (parent_rate && clk_get_parent(hdmi->hdmi_clk)) { |
987 | rate = clk_round_rate(hdmi->hdmi_clk, rate); | 1059 | ret = clk_set_rate(clk_get_parent(hdmi->hdmi_clk), parent_rate); |
988 | if (rate > 0) { | ||
989 | ret = clk_set_rate(hdmi->hdmi_clk, rate); | ||
990 | if (ret < 0) { | 1060 | if (ret < 0) { |
991 | dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", rate, ret); | 1061 | dev_warn(hdmi->dev, "Cannot set parent rate %ld: %d\n", parent_rate, ret); |
992 | rate = 0; | 1062 | hdmi_rate = clk_round_rate(hdmi->hdmi_clk, hdmi_rate); |
993 | } else { | 1063 | } else { |
994 | dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", rate); | 1064 | dev_dbg(hdmi->dev, "HDMI set parent frequency %lu\n", parent_rate); |
995 | } | 1065 | } |
996 | } else { | ||
997 | rate = 0; | ||
998 | dev_warn(hdmi->dev, "Cannot get suitable rate: %ld\n", rate); | ||
999 | } | 1066 | } |
1000 | 1067 | ||
1001 | ret = clk_enable(hdmi->hdmi_clk); | 1068 | ret = clk_set_rate(hdmi->hdmi_clk, hdmi_rate); |
1002 | if (ret < 0) { | 1069 | if (ret < 0) { |
1003 | dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); | 1070 | dev_warn(hdmi->dev, "Cannot set rate %ld: %d\n", hdmi_rate, ret); |
1004 | return ret; | 1071 | hdmi_rate = 0; |
1072 | } else { | ||
1073 | dev_dbg(hdmi->dev, "HDMI set frequency %lu\n", hdmi_rate); | ||
1005 | } | 1074 | } |
1006 | 1075 | ||
1007 | return rate; | 1076 | return hdmi_rate; |
1008 | } | 1077 | } |
1009 | 1078 | ||
1010 | /* Hotplug interrupt occurred, read EDID */ | 1079 | /* Hotplug interrupt occurred, read EDID */ |
@@ -1023,17 +1092,20 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) | |||
1023 | 1092 | ||
1024 | mutex_lock(&hdmi->mutex); | 1093 | mutex_lock(&hdmi->mutex); |
1025 | 1094 | ||
1026 | if (hdmi->hp_state == HDMI_HOTPLUG_EDID_DONE) { | 1095 | if (hdmi->hp_state == HDMI_HOTPLUG_CONNECTED) { |
1096 | unsigned long parent_rate = 0, hdmi_rate; | ||
1097 | |||
1027 | /* A device has been plugged in */ | 1098 | /* A device has been plugged in */ |
1028 | pm_runtime_get_sync(hdmi->dev); | 1099 | pm_runtime_get_sync(hdmi->dev); |
1029 | 1100 | ||
1030 | ret = sh_hdmi_read_edid(hdmi); | 1101 | ret = sh_hdmi_read_edid(hdmi, &hdmi_rate, &parent_rate); |
1031 | if (ret < 0) | 1102 | if (ret < 0) |
1032 | goto out; | 1103 | goto out; |
1033 | 1104 | ||
1105 | hdmi->hp_state = HDMI_HOTPLUG_EDID_DONE; | ||
1106 | |||
1034 | /* Reconfigure the clock */ | 1107 | /* Reconfigure the clock */ |
1035 | clk_disable(hdmi->hdmi_clk); | 1108 | ret = sh_hdmi_clk_configure(hdmi, hdmi_rate, parent_rate); |
1036 | ret = sh_hdmi_clk_configure(hdmi, hdmi->var.pixclock); | ||
1037 | if (ret < 0) | 1109 | if (ret < 0) |
1038 | goto out; | 1110 | goto out; |
1039 | 1111 | ||
@@ -1085,7 +1157,7 @@ static void sh_hdmi_edid_work_fn(struct work_struct *work) | |||
1085 | } | 1157 | } |
1086 | 1158 | ||
1087 | out: | 1159 | out: |
1088 | if (ret < 0) | 1160 | if (ret < 0 && ret != -EAGAIN) |
1089 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; | 1161 | hdmi->hp_state = HDMI_HOTPLUG_DISCONNECTED; |
1090 | mutex_unlock(&hdmi->mutex); | 1162 | mutex_unlock(&hdmi->mutex); |
1091 | 1163 | ||
@@ -1166,13 +1238,22 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
1166 | goto egetclk; | 1238 | goto egetclk; |
1167 | } | 1239 | } |
1168 | 1240 | ||
1169 | /* Some arbitrary relaxed pixclock just to get things started */ | 1241 | /* An arbitrary relaxed pixclock just to get things started: from standard 480p */ |
1170 | rate = sh_hdmi_clk_configure(hdmi, 37037); | 1242 | rate = clk_round_rate(hdmi->hdmi_clk, PICOS2KHZ(37037)); |
1243 | if (rate > 0) | ||
1244 | rate = sh_hdmi_clk_configure(hdmi, rate, 0); | ||
1245 | |||
1171 | if (rate < 0) { | 1246 | if (rate < 0) { |
1172 | ret = rate; | 1247 | ret = rate; |
1173 | goto erate; | 1248 | goto erate; |
1174 | } | 1249 | } |
1175 | 1250 | ||
1251 | ret = clk_enable(hdmi->hdmi_clk); | ||
1252 | if (ret < 0) { | ||
1253 | dev_err(hdmi->dev, "Cannot enable clock: %d\n", ret); | ||
1254 | goto erate; | ||
1255 | } | ||
1256 | |||
1176 | dev_dbg(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); | 1257 | dev_dbg(&pdev->dev, "Enabled HDMI clock at %luHz\n", rate); |
1177 | 1258 | ||
1178 | if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { | 1259 | if (!request_mem_region(res->start, resource_size(res), dev_name(&pdev->dev))) { |
@@ -1190,10 +1271,6 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
1190 | 1271 | ||
1191 | platform_set_drvdata(pdev, hdmi); | 1272 | platform_set_drvdata(pdev, hdmi); |
1192 | 1273 | ||
1193 | /* Product and revision IDs are 0 in sh-mobile version */ | ||
1194 | dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", | ||
1195 | hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); | ||
1196 | |||
1197 | /* Set up LCDC callbacks */ | 1274 | /* Set up LCDC callbacks */ |
1198 | board_cfg = &pdata->lcd_chan->board_cfg; | 1275 | board_cfg = &pdata->lcd_chan->board_cfg; |
1199 | board_cfg->owner = THIS_MODULE; | 1276 | board_cfg->owner = THIS_MODULE; |
@@ -1206,6 +1283,10 @@ static int __init sh_hdmi_probe(struct platform_device *pdev) | |||
1206 | pm_runtime_enable(&pdev->dev); | 1283 | pm_runtime_enable(&pdev->dev); |
1207 | pm_runtime_resume(&pdev->dev); | 1284 | pm_runtime_resume(&pdev->dev); |
1208 | 1285 | ||
1286 | /* Product and revision IDs are 0 in sh-mobile version */ | ||
1287 | dev_info(&pdev->dev, "Detected HDMI controller 0x%x:0x%x\n", | ||
1288 | hdmi_read(hdmi, HDMI_PRODUCT_ID), hdmi_read(hdmi, HDMI_REVISION_ID)); | ||
1289 | |||
1209 | ret = request_irq(irq, sh_hdmi_hotplug, 0, | 1290 | ret = request_irq(irq, sh_hdmi_hotplug, 0, |
1210 | dev_name(&pdev->dev), hdmi); | 1291 | dev_name(&pdev->dev), hdmi); |
1211 | if (ret < 0) { | 1292 | if (ret < 0) { |
diff --git a/drivers/video/sh_mobile_lcdcfb.c b/drivers/video/sh_mobile_lcdcfb.c index b02d97a879d6..778bffbbdbb7 100644 --- a/drivers/video/sh_mobile_lcdcfb.c +++ b/drivers/video/sh_mobile_lcdcfb.c | |||
@@ -54,8 +54,8 @@ static int lcdc_shared_regs[] = { | |||
54 | }; | 54 | }; |
55 | #define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs) | 55 | #define NR_SHARED_REGS ARRAY_SIZE(lcdc_shared_regs) |
56 | 56 | ||
57 | #define DEFAULT_XRES 1280 | 57 | #define MAX_XRES 1920 |
58 | #define DEFAULT_YRES 1024 | 58 | #define MAX_YRES 1080 |
59 | 59 | ||
60 | static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { | 60 | static unsigned long lcdc_offs_mainlcd[NR_CH_REGS] = { |
61 | [LDDCKPAT1R] = 0x400, | 61 | [LDDCKPAT1R] = 0x400, |
@@ -914,22 +914,12 @@ static int sh_mobile_check_var(struct fb_var_screeninfo *var, struct fb_info *in | |||
914 | { | 914 | { |
915 | struct sh_mobile_lcdc_chan *ch = info->par; | 915 | struct sh_mobile_lcdc_chan *ch = info->par; |
916 | 916 | ||
917 | if (var->xres < 160 || var->xres > 1920 || | 917 | if (var->xres > MAX_XRES || var->yres > MAX_YRES || |
918 | var->yres < 120 || var->yres > 1080 || | ||
919 | var->left_margin < 32 || var->left_margin > 320 || | ||
920 | var->right_margin < 12 || var->right_margin > 240 || | ||
921 | var->upper_margin < 12 || var->upper_margin > 120 || | ||
922 | var->lower_margin < 1 || var->lower_margin > 64 || | ||
923 | var->hsync_len < 32 || var->hsync_len > 240 || | ||
924 | var->vsync_len < 2 || var->vsync_len > 64 || | ||
925 | var->pixclock < 6000 || var->pixclock > 40000 || | ||
926 | var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) { | 918 | var->xres * var->yres * (ch->cfg.bpp / 8) * 2 > info->fix.smem_len) { |
927 | dev_warn(info->dev, "Invalid info: %u %u %u %u %u %u %u %u %u!\n", | 919 | dev_warn(info->dev, "Invalid info: %u-%u-%u-%u x %u-%u-%u-%u @ %lukHz!\n", |
928 | var->xres, var->yres, | 920 | var->left_margin, var->xres, var->right_margin, var->hsync_len, |
929 | var->left_margin, var->right_margin, | 921 | var->upper_margin, var->yres, var->lower_margin, var->vsync_len, |
930 | var->upper_margin, var->lower_margin, | 922 | PICOS2KHZ(var->pixclock)); |
931 | var->hsync_len, var->vsync_len, | ||
932 | var->pixclock); | ||
933 | return -EINVAL; | 923 | return -EINVAL; |
934 | } | 924 | } |
935 | return 0; | 925 | return 0; |
@@ -1226,7 +1216,7 @@ static int __devinit sh_mobile_lcdc_probe(struct platform_device *pdev) | |||
1226 | } | 1216 | } |
1227 | 1217 | ||
1228 | if (!mode) | 1218 | if (!mode) |
1229 | max_size = DEFAULT_XRES * DEFAULT_YRES; | 1219 | max_size = MAX_XRES * MAX_YRES; |
1230 | else if (max_cfg) | 1220 | else if (max_cfg) |
1231 | dev_dbg(&pdev->dev, "Found largest videomode %ux%u\n", | 1221 | dev_dbg(&pdev->dev, "Found largest videomode %ux%u\n", |
1232 | max_cfg->xres, max_cfg->yres); | 1222 | max_cfg->xres, max_cfg->yres); |
diff --git a/drivers/video/via/via-core.c b/drivers/video/via/via-core.c index a3aa91709503..6723d6910cde 100644 --- a/drivers/video/via/via-core.c +++ b/drivers/video/via/via-core.c | |||
@@ -15,6 +15,9 @@ | |||
15 | #include <linux/module.h> | 15 | #include <linux/module.h> |
16 | #include <linux/interrupt.h> | 16 | #include <linux/interrupt.h> |
17 | #include <linux/platform_device.h> | 17 | #include <linux/platform_device.h> |
18 | #include <linux/list.h> | ||
19 | #include <linux/pm.h> | ||
20 | #include <asm/olpc.h> | ||
18 | 21 | ||
19 | /* | 22 | /* |
20 | * The default port config. | 23 | * The default port config. |
@@ -29,6 +32,19 @@ static struct via_port_cfg adap_configs[] = { | |||
29 | }; | 32 | }; |
30 | 33 | ||
31 | /* | 34 | /* |
35 | * The OLPC XO-1.5 puts the camera power and reset lines onto | ||
36 | * GPIO 2C. | ||
37 | */ | ||
38 | static const struct via_port_cfg olpc_adap_configs[] = { | ||
39 | [VIA_PORT_26] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x26 }, | ||
40 | [VIA_PORT_31] = { VIA_PORT_I2C, VIA_MODE_I2C, VIASR, 0x31 }, | ||
41 | [VIA_PORT_25] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x25 }, | ||
42 | [VIA_PORT_2C] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x2c }, | ||
43 | [VIA_PORT_3D] = { VIA_PORT_GPIO, VIA_MODE_GPIO, VIASR, 0x3d }, | ||
44 | { 0, 0, 0, 0 } | ||
45 | }; | ||
46 | |||
47 | /* | ||
32 | * We currently only support one viafb device (will there ever be | 48 | * We currently only support one viafb device (will there ever be |
33 | * more than one?), so just declare it globally here. | 49 | * more than one?), so just declare it globally here. |
34 | */ | 50 | */ |
@@ -575,6 +591,78 @@ static void via_teardown_subdevs(void) | |||
575 | } | 591 | } |
576 | } | 592 | } |
577 | 593 | ||
594 | /* | ||
595 | * Power management functions | ||
596 | */ | ||
597 | #ifdef CONFIG_PM | ||
598 | static LIST_HEAD(viafb_pm_hooks); | ||
599 | static DEFINE_MUTEX(viafb_pm_hooks_lock); | ||
600 | |||
601 | void viafb_pm_register(struct viafb_pm_hooks *hooks) | ||
602 | { | ||
603 | INIT_LIST_HEAD(&hooks->list); | ||
604 | |||
605 | mutex_lock(&viafb_pm_hooks_lock); | ||
606 | list_add_tail(&hooks->list, &viafb_pm_hooks); | ||
607 | mutex_unlock(&viafb_pm_hooks_lock); | ||
608 | } | ||
609 | EXPORT_SYMBOL_GPL(viafb_pm_register); | ||
610 | |||
611 | void viafb_pm_unregister(struct viafb_pm_hooks *hooks) | ||
612 | { | ||
613 | mutex_lock(&viafb_pm_hooks_lock); | ||
614 | list_del(&hooks->list); | ||
615 | mutex_unlock(&viafb_pm_hooks_lock); | ||
616 | } | ||
617 | EXPORT_SYMBOL_GPL(viafb_pm_unregister); | ||
618 | |||
619 | static int via_suspend(struct pci_dev *pdev, pm_message_t state) | ||
620 | { | ||
621 | struct viafb_pm_hooks *hooks; | ||
622 | |||
623 | if (state.event != PM_EVENT_SUSPEND) | ||
624 | return 0; | ||
625 | /* | ||
626 | * "I've occasionally hit a few drivers that caused suspend | ||
627 | * failures, and each and every time it was a driver bug, and | ||
628 | * the right thing to do was to just ignore the error and suspend | ||
629 | * anyway - returning an error code and trying to undo the suspend | ||
630 | * is not what anybody ever really wants, even if our model | ||
631 | *_allows_ for it." | ||
632 | * -- Linus Torvalds, Dec. 7, 2009 | ||
633 | */ | ||
634 | mutex_lock(&viafb_pm_hooks_lock); | ||
635 | list_for_each_entry_reverse(hooks, &viafb_pm_hooks, list) | ||
636 | hooks->suspend(hooks->private); | ||
637 | mutex_unlock(&viafb_pm_hooks_lock); | ||
638 | |||
639 | pci_save_state(pdev); | ||
640 | pci_disable_device(pdev); | ||
641 | pci_set_power_state(pdev, pci_choose_state(pdev, state)); | ||
642 | return 0; | ||
643 | } | ||
644 | |||
645 | static int via_resume(struct pci_dev *pdev) | ||
646 | { | ||
647 | struct viafb_pm_hooks *hooks; | ||
648 | |||
649 | /* Get the bus side powered up */ | ||
650 | pci_set_power_state(pdev, PCI_D0); | ||
651 | pci_restore_state(pdev); | ||
652 | if (pci_enable_device(pdev)) | ||
653 | return 0; | ||
654 | |||
655 | pci_set_master(pdev); | ||
656 | |||
657 | /* Now bring back any subdevs */ | ||
658 | mutex_lock(&viafb_pm_hooks_lock); | ||
659 | list_for_each_entry(hooks, &viafb_pm_hooks, list) | ||
660 | hooks->resume(hooks->private); | ||
661 | mutex_unlock(&viafb_pm_hooks_lock); | ||
662 | |||
663 | return 0; | ||
664 | } | ||
665 | #endif /* CONFIG_PM */ | ||
578 | 666 | ||
579 | static int __devinit via_pci_probe(struct pci_dev *pdev, | 667 | static int __devinit via_pci_probe(struct pci_dev *pdev, |
580 | const struct pci_device_id *ent) | 668 | const struct pci_device_id *ent) |
@@ -584,6 +672,7 @@ static int __devinit via_pci_probe(struct pci_dev *pdev, | |||
584 | ret = pci_enable_device(pdev); | 672 | ret = pci_enable_device(pdev); |
585 | if (ret) | 673 | if (ret) |
586 | return ret; | 674 | return ret; |
675 | |||
587 | /* | 676 | /* |
588 | * Global device initialization. | 677 | * Global device initialization. |
589 | */ | 678 | */ |
@@ -591,6 +680,9 @@ static int __devinit via_pci_probe(struct pci_dev *pdev, | |||
591 | global_dev.pdev = pdev; | 680 | global_dev.pdev = pdev; |
592 | global_dev.chip_type = ent->driver_data; | 681 | global_dev.chip_type = ent->driver_data; |
593 | global_dev.port_cfg = adap_configs; | 682 | global_dev.port_cfg = adap_configs; |
683 | if (machine_is_olpc()) | ||
684 | global_dev.port_cfg = olpc_adap_configs; | ||
685 | |||
594 | spin_lock_init(&global_dev.reg_lock); | 686 | spin_lock_init(&global_dev.reg_lock); |
595 | ret = via_pci_setup_mmio(&global_dev); | 687 | ret = via_pci_setup_mmio(&global_dev); |
596 | if (ret) | 688 | if (ret) |
@@ -663,8 +755,8 @@ static struct pci_driver via_driver = { | |||
663 | .probe = via_pci_probe, | 755 | .probe = via_pci_probe, |
664 | .remove = __devexit_p(via_pci_remove), | 756 | .remove = __devexit_p(via_pci_remove), |
665 | #ifdef CONFIG_PM | 757 | #ifdef CONFIG_PM |
666 | .suspend = viafb_suspend, | 758 | .suspend = via_suspend, |
667 | .resume = viafb_resume, | 759 | .resume = via_resume, |
668 | #endif | 760 | #endif |
669 | }; | 761 | }; |
670 | 762 | ||
diff --git a/drivers/video/via/via-gpio.c b/drivers/video/via/via-gpio.c index 39acb37e7a1d..c2a0a1cfd3b3 100644 --- a/drivers/video/via/via-gpio.c +++ b/drivers/video/via/via-gpio.c | |||
@@ -172,6 +172,28 @@ static void viafb_gpio_disable(struct viafb_gpio *gpio) | |||
172 | via_write_reg_mask(VIASR, gpio->vg_port_index, 0, 0x02); | 172 | via_write_reg_mask(VIASR, gpio->vg_port_index, 0, 0x02); |
173 | } | 173 | } |
174 | 174 | ||
175 | #ifdef CONFIG_PM | ||
176 | |||
177 | static int viafb_gpio_suspend(void *private) | ||
178 | { | ||
179 | return 0; | ||
180 | } | ||
181 | |||
182 | static int viafb_gpio_resume(void *private) | ||
183 | { | ||
184 | int i; | ||
185 | |||
186 | for (i = 0; i < gpio_config.gpio_chip.ngpio; i += 2) | ||
187 | viafb_gpio_enable(gpio_config.active_gpios[i]); | ||
188 | return 0; | ||
189 | } | ||
190 | |||
191 | static struct viafb_pm_hooks viafb_gpio_pm_hooks = { | ||
192 | .suspend = viafb_gpio_suspend, | ||
193 | .resume = viafb_gpio_resume | ||
194 | }; | ||
195 | #endif /* CONFIG_PM */ | ||
196 | |||
175 | /* | 197 | /* |
176 | * Look up a specific gpio and return the number it was assigned. | 198 | * Look up a specific gpio and return the number it was assigned. |
177 | */ | 199 | */ |
@@ -236,6 +258,9 @@ static __devinit int viafb_gpio_probe(struct platform_device *platdev) | |||
236 | printk(KERN_ERR "viafb: failed to add gpios (%d)\n", ret); | 258 | printk(KERN_ERR "viafb: failed to add gpios (%d)\n", ret); |
237 | gpio_config.gpio_chip.ngpio = 0; | 259 | gpio_config.gpio_chip.ngpio = 0; |
238 | } | 260 | } |
261 | #ifdef CONFIG_PM | ||
262 | viafb_pm_register(&viafb_gpio_pm_hooks); | ||
263 | #endif | ||
239 | return ret; | 264 | return ret; |
240 | } | 265 | } |
241 | 266 | ||
@@ -245,6 +270,10 @@ static int viafb_gpio_remove(struct platform_device *platdev) | |||
245 | unsigned long flags; | 270 | unsigned long flags; |
246 | int ret = 0, i; | 271 | int ret = 0, i; |
247 | 272 | ||
273 | #ifdef CONFIG_PM | ||
274 | viafb_pm_unregister(&viafb_gpio_pm_hooks); | ||
275 | #endif | ||
276 | |||
248 | /* | 277 | /* |
249 | * Get unregistered. | 278 | * Get unregistered. |
250 | */ | 279 | */ |
diff --git a/drivers/video/via/viafbdev.c b/drivers/video/via/viafbdev.c index d298cfccd6fc..289edd519527 100644 --- a/drivers/video/via/viafbdev.c +++ b/drivers/video/via/viafbdev.c | |||
@@ -1672,31 +1672,19 @@ static int parse_mode(const char *str, u32 *xres, u32 *yres) | |||
1672 | 1672 | ||
1673 | 1673 | ||
1674 | #ifdef CONFIG_PM | 1674 | #ifdef CONFIG_PM |
1675 | int viafb_suspend(struct pci_dev *pdev, pm_message_t state) | 1675 | static int viafb_suspend(void *unused) |
1676 | { | 1676 | { |
1677 | if (state.event == PM_EVENT_SUSPEND) { | 1677 | acquire_console_sem(); |
1678 | acquire_console_sem(); | 1678 | fb_set_suspend(viafbinfo, 1); |
1679 | fb_set_suspend(viafbinfo, 1); | 1679 | viafb_sync(viafbinfo); |
1680 | 1680 | release_console_sem(); | |
1681 | viafb_sync(viafbinfo); | ||
1682 | |||
1683 | pci_save_state(pdev); | ||
1684 | pci_disable_device(pdev); | ||
1685 | pci_set_power_state(pdev, pci_choose_state(pdev, state)); | ||
1686 | release_console_sem(); | ||
1687 | } | ||
1688 | 1681 | ||
1689 | return 0; | 1682 | return 0; |
1690 | } | 1683 | } |
1691 | 1684 | ||
1692 | int viafb_resume(struct pci_dev *pdev) | 1685 | static int viafb_resume(void *unused) |
1693 | { | 1686 | { |
1694 | acquire_console_sem(); | 1687 | acquire_console_sem(); |
1695 | pci_set_power_state(pdev, PCI_D0); | ||
1696 | pci_restore_state(pdev); | ||
1697 | if (pci_enable_device(pdev)) | ||
1698 | goto fail; | ||
1699 | pci_set_master(pdev); | ||
1700 | if (viaparinfo->shared->vdev->engine_mmio) | 1688 | if (viaparinfo->shared->vdev->engine_mmio) |
1701 | viafb_reset_engine(viaparinfo); | 1689 | viafb_reset_engine(viaparinfo); |
1702 | viafb_set_par(viafbinfo); | 1690 | viafb_set_par(viafbinfo); |
@@ -1704,11 +1692,15 @@ int viafb_resume(struct pci_dev *pdev) | |||
1704 | viafb_set_par(viafbinfo1); | 1692 | viafb_set_par(viafbinfo1); |
1705 | fb_set_suspend(viafbinfo, 0); | 1693 | fb_set_suspend(viafbinfo, 0); |
1706 | 1694 | ||
1707 | fail: | ||
1708 | release_console_sem(); | 1695 | release_console_sem(); |
1709 | return 0; | 1696 | return 0; |
1710 | } | 1697 | } |
1711 | 1698 | ||
1699 | static struct viafb_pm_hooks viafb_fb_pm_hooks = { | ||
1700 | .suspend = viafb_suspend, | ||
1701 | .resume = viafb_resume | ||
1702 | }; | ||
1703 | |||
1712 | #endif | 1704 | #endif |
1713 | 1705 | ||
1714 | 1706 | ||
@@ -1899,6 +1891,10 @@ int __devinit via_fb_pci_probe(struct viafb_dev *vdev) | |||
1899 | 1891 | ||
1900 | viafb_init_proc(viaparinfo->shared); | 1892 | viafb_init_proc(viaparinfo->shared); |
1901 | viafb_init_dac(IGA2); | 1893 | viafb_init_dac(IGA2); |
1894 | |||
1895 | #ifdef CONFIG_PM | ||
1896 | viafb_pm_register(&viafb_fb_pm_hooks); | ||
1897 | #endif | ||
1902 | return 0; | 1898 | return 0; |
1903 | 1899 | ||
1904 | out_fb_unreg: | 1900 | out_fb_unreg: |
diff --git a/drivers/video/via/viafbdev.h b/drivers/video/via/viafbdev.h index 4960e3da6645..d66f963e930e 100644 --- a/drivers/video/via/viafbdev.h +++ b/drivers/video/via/viafbdev.h | |||
@@ -108,6 +108,4 @@ void via_fb_pci_remove(struct pci_dev *pdev); | |||
108 | /* Temporary */ | 108 | /* Temporary */ |
109 | int viafb_init(void); | 109 | int viafb_init(void); |
110 | void viafb_exit(void); | 110 | void viafb_exit(void); |
111 | int viafb_suspend(struct pci_dev *pdev, pm_message_t state); | ||
112 | int viafb_resume(struct pci_dev *pdev); | ||
113 | #endif /* __VIAFBDEV_H__ */ | 111 | #endif /* __VIAFBDEV_H__ */ |
diff --git a/drivers/video/vt8500lcdfb.c b/drivers/video/vt8500lcdfb.c new file mode 100644 index 000000000000..7617f12e4fd7 --- /dev/null +++ b/drivers/video/vt8500lcdfb.c | |||
@@ -0,0 +1,447 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/vt8500lcdfb.c | ||
3 | * | ||
4 | * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> | ||
5 | * | ||
6 | * Based on skeletonfb.c and pxafb.c | ||
7 | * | ||
8 | * This software is licensed under the terms of the GNU General Public | ||
9 | * License version 2, as published by the Free Software Foundation, and | ||
10 | * may be copied, distributed, and modified under those terms. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/module.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/errno.h> | ||
21 | #include <linux/string.h> | ||
22 | #include <linux/mm.h> | ||
23 | #include <linux/slab.h> | ||
24 | #include <linux/delay.h> | ||
25 | #include <linux/fb.h> | ||
26 | #include <linux/init.h> | ||
27 | #include <linux/interrupt.h> | ||
28 | #include <linux/io.h> | ||
29 | #include <linux/dma-mapping.h> | ||
30 | #include <linux/platform_device.h> | ||
31 | #include <linux/wait.h> | ||
32 | |||
33 | #include <mach/vt8500fb.h> | ||
34 | |||
35 | #include "vt8500lcdfb.h" | ||
36 | #include "wmt_ge_rops.h" | ||
37 | |||
38 | #define to_vt8500lcd_info(__info) container_of(__info, \ | ||
39 | struct vt8500lcd_info, fb) | ||
40 | |||
41 | static int vt8500lcd_set_par(struct fb_info *info) | ||
42 | { | ||
43 | struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); | ||
44 | int reg_bpp = 5; /* 16bpp */ | ||
45 | int i; | ||
46 | unsigned long control0; | ||
47 | |||
48 | if (!fbi) | ||
49 | return -EINVAL; | ||
50 | |||
51 | if (info->var.bits_per_pixel <= 8) { | ||
52 | /* palettized */ | ||
53 | info->var.red.offset = 0; | ||
54 | info->var.red.length = info->var.bits_per_pixel; | ||
55 | info->var.red.msb_right = 0; | ||
56 | |||
57 | info->var.green.offset = 0; | ||
58 | info->var.green.length = info->var.bits_per_pixel; | ||
59 | info->var.green.msb_right = 0; | ||
60 | |||
61 | info->var.blue.offset = 0; | ||
62 | info->var.blue.length = info->var.bits_per_pixel; | ||
63 | info->var.blue.msb_right = 0; | ||
64 | |||
65 | info->var.transp.offset = 0; | ||
66 | info->var.transp.length = 0; | ||
67 | info->var.transp.msb_right = 0; | ||
68 | |||
69 | info->fix.visual = FB_VISUAL_PSEUDOCOLOR; | ||
70 | info->fix.line_length = info->var.xres_virtual / | ||
71 | (8/info->var.bits_per_pixel); | ||
72 | } else { | ||
73 | /* non-palettized */ | ||
74 | info->var.transp.offset = 0; | ||
75 | info->var.transp.length = 0; | ||
76 | info->var.transp.msb_right = 0; | ||
77 | |||
78 | if (info->var.bits_per_pixel == 16) { | ||
79 | /* RGB565 */ | ||
80 | info->var.red.offset = 11; | ||
81 | info->var.red.length = 5; | ||
82 | info->var.red.msb_right = 0; | ||
83 | info->var.green.offset = 5; | ||
84 | info->var.green.length = 6; | ||
85 | info->var.green.msb_right = 0; | ||
86 | info->var.blue.offset = 0; | ||
87 | info->var.blue.length = 5; | ||
88 | info->var.blue.msb_right = 0; | ||
89 | } else { | ||
90 | /* Equal depths per channel */ | ||
91 | info->var.red.offset = info->var.bits_per_pixel | ||
92 | * 2 / 3; | ||
93 | info->var.red.length = info->var.bits_per_pixel / 3; | ||
94 | info->var.red.msb_right = 0; | ||
95 | info->var.green.offset = info->var.bits_per_pixel / 3; | ||
96 | info->var.green.length = info->var.bits_per_pixel / 3; | ||
97 | info->var.green.msb_right = 0; | ||
98 | info->var.blue.offset = 0; | ||
99 | info->var.blue.length = info->var.bits_per_pixel / 3; | ||
100 | info->var.blue.msb_right = 0; | ||
101 | } | ||
102 | |||
103 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
104 | info->fix.line_length = info->var.bits_per_pixel > 16 ? | ||
105 | info->var.xres_virtual << 2 : | ||
106 | info->var.xres_virtual << 1; | ||
107 | } | ||
108 | |||
109 | for (i = 0; i < 8; i++) { | ||
110 | if (bpp_values[i] == info->var.bits_per_pixel) { | ||
111 | reg_bpp = i; | ||
112 | continue; | ||
113 | } | ||
114 | } | ||
115 | |||
116 | control0 = readl(fbi->regbase) & ~0xf; | ||
117 | writel(0, fbi->regbase); | ||
118 | while (readl(fbi->regbase + 0x38) & 0x10) | ||
119 | /* wait */; | ||
120 | writel((((info->var.hsync_len - 1) & 0x3f) << 26) | ||
121 | | ((info->var.left_margin & 0xff) << 18) | ||
122 | | (((info->var.xres - 1) & 0x3ff) << 8) | ||
123 | | (info->var.right_margin & 0xff), fbi->regbase + 0x4); | ||
124 | writel((((info->var.vsync_len - 1) & 0x3f) << 26) | ||
125 | | ((info->var.upper_margin & 0xff) << 18) | ||
126 | | (((info->var.yres - 1) & 0x3ff) << 8) | ||
127 | | (info->var.lower_margin & 0xff), fbi->regbase + 0x8); | ||
128 | writel((((info->var.yres - 1) & 0x400) << 2) | ||
129 | | ((info->var.xres - 1) & 0x400), fbi->regbase + 0x10); | ||
130 | writel(0x80000000, fbi->regbase + 0x20); | ||
131 | writel(control0 | (reg_bpp << 1) | 0x100, fbi->regbase); | ||
132 | |||
133 | return 0; | ||
134 | } | ||
135 | |||
136 | static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) | ||
137 | { | ||
138 | chan &= 0xffff; | ||
139 | chan >>= 16 - bf->length; | ||
140 | return chan << bf->offset; | ||
141 | } | ||
142 | |||
143 | static int vt8500lcd_setcolreg(unsigned regno, unsigned red, unsigned green, | ||
144 | unsigned blue, unsigned transp, | ||
145 | struct fb_info *info) { | ||
146 | struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); | ||
147 | int ret = 1; | ||
148 | unsigned int val; | ||
149 | if (regno >= 256) | ||
150 | return -EINVAL; | ||
151 | |||
152 | if (info->var.grayscale) | ||
153 | red = green = blue = | ||
154 | (19595 * red + 38470 * green + 7471 * blue) >> 16; | ||
155 | |||
156 | switch (fbi->fb.fix.visual) { | ||
157 | case FB_VISUAL_TRUECOLOR: | ||
158 | if (regno < 16) { | ||
159 | u32 *pal = fbi->fb.pseudo_palette; | ||
160 | |||
161 | val = chan_to_field(red, &fbi->fb.var.red); | ||
162 | val |= chan_to_field(green, &fbi->fb.var.green); | ||
163 | val |= chan_to_field(blue, &fbi->fb.var.blue); | ||
164 | |||
165 | pal[regno] = val; | ||
166 | ret = 0; | ||
167 | } | ||
168 | break; | ||
169 | |||
170 | case FB_VISUAL_STATIC_PSEUDOCOLOR: | ||
171 | case FB_VISUAL_PSEUDOCOLOR: | ||
172 | writew((red & 0xf800) | ||
173 | | ((green >> 5) & 0x7e0) | ||
174 | | ((blue >> 11) & 0x1f), | ||
175 | fbi->palette_cpu + sizeof(u16) * regno); | ||
176 | break; | ||
177 | } | ||
178 | |||
179 | return ret; | ||
180 | } | ||
181 | |||
182 | static int vt8500lcd_ioctl(struct fb_info *info, unsigned int cmd, | ||
183 | unsigned long arg) | ||
184 | { | ||
185 | int ret = 0; | ||
186 | struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); | ||
187 | |||
188 | if (cmd == FBIO_WAITFORVSYNC) { | ||
189 | /* Unmask End of Frame interrupt */ | ||
190 | writel(0xffffffff ^ (1 << 3), fbi->regbase + 0x3c); | ||
191 | ret = wait_event_interruptible_timeout(fbi->wait, | ||
192 | readl(fbi->regbase + 0x38) & (1 << 3), HZ / 10); | ||
193 | /* Mask back to reduce unwanted interrupt traffic */ | ||
194 | writel(0xffffffff, fbi->regbase + 0x3c); | ||
195 | if (ret < 0) | ||
196 | return ret; | ||
197 | if (ret == 0) | ||
198 | return -ETIMEDOUT; | ||
199 | } | ||
200 | |||
201 | return ret; | ||
202 | } | ||
203 | |||
204 | static int vt8500lcd_pan_display(struct fb_var_screeninfo *var, | ||
205 | struct fb_info *info) | ||
206 | { | ||
207 | unsigned pixlen = info->fix.line_length / info->var.xres_virtual; | ||
208 | unsigned off = pixlen * var->xoffset | ||
209 | + info->fix.line_length * var->yoffset; | ||
210 | struct vt8500lcd_info *fbi = to_vt8500lcd_info(info); | ||
211 | |||
212 | writel((1 << 31) | ||
213 | | (((var->xres_virtual - var->xres) * pixlen / 4) << 20) | ||
214 | | (off >> 2), fbi->regbase + 0x20); | ||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static struct fb_ops vt8500lcd_ops = { | ||
219 | .owner = THIS_MODULE, | ||
220 | .fb_set_par = vt8500lcd_set_par, | ||
221 | .fb_setcolreg = vt8500lcd_setcolreg, | ||
222 | .fb_fillrect = wmt_ge_fillrect, | ||
223 | .fb_copyarea = wmt_ge_copyarea, | ||
224 | .fb_imageblit = sys_imageblit, | ||
225 | .fb_sync = wmt_ge_sync, | ||
226 | .fb_ioctl = vt8500lcd_ioctl, | ||
227 | .fb_pan_display = vt8500lcd_pan_display, | ||
228 | }; | ||
229 | |||
230 | static irqreturn_t vt8500lcd_handle_irq(int irq, void *dev_id) | ||
231 | { | ||
232 | struct vt8500lcd_info *fbi = dev_id; | ||
233 | |||
234 | if (readl(fbi->regbase + 0x38) & (1 << 3)) | ||
235 | wake_up_interruptible(&fbi->wait); | ||
236 | |||
237 | writel(0xffffffff, fbi->regbase + 0x38); | ||
238 | return IRQ_HANDLED; | ||
239 | } | ||
240 | |||
241 | static int __devinit vt8500lcd_probe(struct platform_device *pdev) | ||
242 | { | ||
243 | struct vt8500lcd_info *fbi; | ||
244 | struct resource *res; | ||
245 | struct vt8500fb_platform_data *pdata = pdev->dev.platform_data; | ||
246 | void *addr; | ||
247 | int irq, ret; | ||
248 | |||
249 | ret = -ENOMEM; | ||
250 | fbi = NULL; | ||
251 | |||
252 | fbi = kzalloc(sizeof(struct vt8500lcd_info) + sizeof(u32) * 16, | ||
253 | GFP_KERNEL); | ||
254 | if (!fbi) { | ||
255 | dev_err(&pdev->dev, "Failed to initialize framebuffer device\n"); | ||
256 | ret = -ENOMEM; | ||
257 | goto failed; | ||
258 | } | ||
259 | |||
260 | strcpy(fbi->fb.fix.id, "VT8500 LCD"); | ||
261 | |||
262 | fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; | ||
263 | fbi->fb.fix.xpanstep = 0; | ||
264 | fbi->fb.fix.ypanstep = 1; | ||
265 | fbi->fb.fix.ywrapstep = 0; | ||
266 | fbi->fb.fix.accel = FB_ACCEL_NONE; | ||
267 | |||
268 | fbi->fb.var.nonstd = 0; | ||
269 | fbi->fb.var.activate = FB_ACTIVATE_NOW; | ||
270 | fbi->fb.var.height = -1; | ||
271 | fbi->fb.var.width = -1; | ||
272 | fbi->fb.var.vmode = FB_VMODE_NONINTERLACED; | ||
273 | |||
274 | fbi->fb.fbops = &vt8500lcd_ops; | ||
275 | fbi->fb.flags = FBINFO_DEFAULT | ||
276 | | FBINFO_HWACCEL_COPYAREA | ||
277 | | FBINFO_HWACCEL_FILLRECT | ||
278 | | FBINFO_HWACCEL_YPAN | ||
279 | | FBINFO_VIRTFB | ||
280 | | FBINFO_PARTIAL_PAN_OK; | ||
281 | fbi->fb.node = -1; | ||
282 | |||
283 | addr = fbi; | ||
284 | addr = addr + sizeof(struct vt8500lcd_info); | ||
285 | fbi->fb.pseudo_palette = addr; | ||
286 | |||
287 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
288 | if (res == NULL) { | ||
289 | dev_err(&pdev->dev, "no I/O memory resource defined\n"); | ||
290 | ret = -ENODEV; | ||
291 | goto failed_fbi; | ||
292 | } | ||
293 | |||
294 | res = request_mem_region(res->start, resource_size(res), "vt8500lcd"); | ||
295 | if (res == NULL) { | ||
296 | dev_err(&pdev->dev, "failed to request I/O memory\n"); | ||
297 | ret = -EBUSY; | ||
298 | goto failed_fbi; | ||
299 | } | ||
300 | |||
301 | fbi->regbase = ioremap(res->start, resource_size(res)); | ||
302 | if (fbi->regbase == NULL) { | ||
303 | dev_err(&pdev->dev, "failed to map I/O memory\n"); | ||
304 | ret = -EBUSY; | ||
305 | goto failed_free_res; | ||
306 | } | ||
307 | |||
308 | fbi->fb.fix.smem_start = pdata->video_mem_phys; | ||
309 | fbi->fb.fix.smem_len = pdata->video_mem_len; | ||
310 | fbi->fb.screen_base = pdata->video_mem_virt; | ||
311 | |||
312 | fbi->palette_size = PAGE_ALIGN(512); | ||
313 | fbi->palette_cpu = dma_alloc_coherent(&pdev->dev, | ||
314 | fbi->palette_size, | ||
315 | &fbi->palette_phys, | ||
316 | GFP_KERNEL); | ||
317 | if (fbi->palette_cpu == NULL) { | ||
318 | dev_err(&pdev->dev, "Failed to allocate palette buffer\n"); | ||
319 | ret = -ENOMEM; | ||
320 | goto failed_free_io; | ||
321 | } | ||
322 | |||
323 | irq = platform_get_irq(pdev, 0); | ||
324 | if (irq < 0) { | ||
325 | dev_err(&pdev->dev, "no IRQ defined\n"); | ||
326 | ret = -ENODEV; | ||
327 | goto failed_free_palette; | ||
328 | } | ||
329 | |||
330 | ret = request_irq(irq, vt8500lcd_handle_irq, IRQF_DISABLED, "LCD", fbi); | ||
331 | if (ret) { | ||
332 | dev_err(&pdev->dev, "request_irq failed: %d\n", ret); | ||
333 | ret = -EBUSY; | ||
334 | goto failed_free_palette; | ||
335 | } | ||
336 | |||
337 | init_waitqueue_head(&fbi->wait); | ||
338 | |||
339 | if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) { | ||
340 | dev_err(&pdev->dev, "Failed to allocate color map\n"); | ||
341 | ret = -ENOMEM; | ||
342 | goto failed_free_irq; | ||
343 | } | ||
344 | |||
345 | fb_videomode_to_var(&fbi->fb.var, &pdata->mode); | ||
346 | fbi->fb.var.bits_per_pixel = pdata->bpp; | ||
347 | fbi->fb.var.xres_virtual = pdata->xres_virtual; | ||
348 | fbi->fb.var.yres_virtual = pdata->yres_virtual; | ||
349 | |||
350 | ret = vt8500lcd_set_par(&fbi->fb); | ||
351 | if (ret) { | ||
352 | dev_err(&pdev->dev, "Failed to set parameters\n"); | ||
353 | goto failed_free_cmap; | ||
354 | } | ||
355 | |||
356 | writel(fbi->fb.fix.smem_start >> 22, fbi->regbase + 0x1c); | ||
357 | writel((fbi->palette_phys & 0xfffffe00) | 1, fbi->regbase + 0x18); | ||
358 | |||
359 | platform_set_drvdata(pdev, fbi); | ||
360 | |||
361 | ret = register_framebuffer(&fbi->fb); | ||
362 | if (ret < 0) { | ||
363 | dev_err(&pdev->dev, | ||
364 | "Failed to register framebuffer device: %d\n", ret); | ||
365 | goto failed_free_cmap; | ||
366 | } | ||
367 | |||
368 | /* | ||
369 | * Ok, now enable the LCD controller | ||
370 | */ | ||
371 | writel(readl(fbi->regbase) | 1, fbi->regbase); | ||
372 | |||
373 | return 0; | ||
374 | |||
375 | failed_free_cmap: | ||
376 | if (fbi->fb.cmap.len) | ||
377 | fb_dealloc_cmap(&fbi->fb.cmap); | ||
378 | failed_free_irq: | ||
379 | free_irq(irq, fbi); | ||
380 | failed_free_palette: | ||
381 | dma_free_coherent(&pdev->dev, fbi->palette_size, | ||
382 | fbi->palette_cpu, fbi->palette_phys); | ||
383 | failed_free_io: | ||
384 | iounmap(fbi->regbase); | ||
385 | failed_free_res: | ||
386 | release_mem_region(res->start, resource_size(res)); | ||
387 | failed_fbi: | ||
388 | platform_set_drvdata(pdev, NULL); | ||
389 | kfree(fbi); | ||
390 | failed: | ||
391 | return ret; | ||
392 | } | ||
393 | |||
394 | static int __devexit vt8500lcd_remove(struct platform_device *pdev) | ||
395 | { | ||
396 | struct vt8500lcd_info *fbi = platform_get_drvdata(pdev); | ||
397 | struct resource *res; | ||
398 | int irq; | ||
399 | |||
400 | unregister_framebuffer(&fbi->fb); | ||
401 | |||
402 | writel(0, fbi->regbase); | ||
403 | |||
404 | if (fbi->fb.cmap.len) | ||
405 | fb_dealloc_cmap(&fbi->fb.cmap); | ||
406 | |||
407 | irq = platform_get_irq(pdev, 0); | ||
408 | free_irq(irq, fbi); | ||
409 | |||
410 | dma_free_coherent(&pdev->dev, fbi->palette_size, | ||
411 | fbi->palette_cpu, fbi->palette_phys); | ||
412 | |||
413 | iounmap(fbi->regbase); | ||
414 | |||
415 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
416 | release_mem_region(res->start, resource_size(res)); | ||
417 | |||
418 | kfree(fbi); | ||
419 | |||
420 | return 0; | ||
421 | } | ||
422 | |||
423 | static struct platform_driver vt8500lcd_driver = { | ||
424 | .probe = vt8500lcd_probe, | ||
425 | .remove = __devexit_p(vt8500lcd_remove), | ||
426 | .driver = { | ||
427 | .owner = THIS_MODULE, | ||
428 | .name = "vt8500-lcd", | ||
429 | }, | ||
430 | }; | ||
431 | |||
432 | static int __init vt8500lcd_init(void) | ||
433 | { | ||
434 | return platform_driver_register(&vt8500lcd_driver); | ||
435 | } | ||
436 | |||
437 | static void __exit vt8500lcd_exit(void) | ||
438 | { | ||
439 | platform_driver_unregister(&vt8500lcd_driver); | ||
440 | } | ||
441 | |||
442 | module_init(vt8500lcd_init); | ||
443 | module_exit(vt8500lcd_exit); | ||
444 | |||
445 | MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com>"); | ||
446 | MODULE_DESCRIPTION("LCD controller driver for VIA VT8500"); | ||
447 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/vt8500lcdfb.h b/drivers/video/vt8500lcdfb.h new file mode 100644 index 000000000000..36ca3ca09d83 --- /dev/null +++ b/drivers/video/vt8500lcdfb.h | |||
@@ -0,0 +1,34 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/vt8500lcdfb.h | ||
3 | * | ||
4 | * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | */ | ||
15 | |||
16 | struct vt8500lcd_info { | ||
17 | struct fb_info fb; | ||
18 | void __iomem *regbase; | ||
19 | void __iomem *palette_cpu; | ||
20 | dma_addr_t palette_phys; | ||
21 | size_t palette_size; | ||
22 | wait_queue_head_t wait; | ||
23 | }; | ||
24 | |||
25 | static int bpp_values[] = { | ||
26 | 1, | ||
27 | 2, | ||
28 | 4, | ||
29 | 8, | ||
30 | 12, | ||
31 | 16, | ||
32 | 18, | ||
33 | 24, | ||
34 | }; | ||
diff --git a/drivers/video/wm8505fb.c b/drivers/video/wm8505fb.c new file mode 100644 index 000000000000..96e34a569169 --- /dev/null +++ b/drivers/video/wm8505fb.c | |||
@@ -0,0 +1,422 @@ | |||
1 | /* | ||
2 | * WonderMedia WM8505 Frame Buffer device driver | ||
3 | * | ||
4 | * Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com> | ||
5 | * Based on vt8500lcdfb.c | ||
6 | * | ||
7 | * This software is licensed under the terms of the GNU General Public | ||
8 | * License version 2, as published by the Free Software Foundation, and | ||
9 | * may be copied, distributed, and modified under those terms. | ||
10 | * | ||
11 | * This program is distributed in the hope that it will be useful, | ||
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
14 | * GNU General Public License for more details. | ||
15 | */ | ||
16 | |||
17 | #include <linux/module.h> | ||
18 | #include <linux/kernel.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/string.h> | ||
21 | #include <linux/mm.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/delay.h> | ||
24 | #include <linux/fb.h> | ||
25 | #include <linux/init.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/io.h> | ||
28 | #include <linux/dma-mapping.h> | ||
29 | #include <linux/platform_device.h> | ||
30 | #include <linux/wait.h> | ||
31 | |||
32 | #include <mach/vt8500fb.h> | ||
33 | |||
34 | #include "wm8505fb_regs.h" | ||
35 | #include "wmt_ge_rops.h" | ||
36 | |||
37 | #define DRIVER_NAME "wm8505-fb" | ||
38 | |||
39 | #define to_wm8505fb_info(__info) container_of(__info, \ | ||
40 | struct wm8505fb_info, fb) | ||
41 | struct wm8505fb_info { | ||
42 | struct fb_info fb; | ||
43 | void __iomem *regbase; | ||
44 | unsigned int contrast; | ||
45 | }; | ||
46 | |||
47 | |||
48 | static int wm8505fb_init_hw(struct fb_info *info) | ||
49 | { | ||
50 | struct wm8505fb_info *fbi = to_wm8505fb_info(info); | ||
51 | |||
52 | int i; | ||
53 | |||
54 | /* I know the purpose only of few registers, so clear unknown */ | ||
55 | for (i = 0; i < 0x200; i += 4) | ||
56 | writel(0, fbi->regbase + i); | ||
57 | |||
58 | /* Set frame buffer address */ | ||
59 | writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR); | ||
60 | writel(fbi->fb.fix.smem_start, fbi->regbase + WMT_GOVR_FBADDR1); | ||
61 | |||
62 | /* Set in-memory picture format to RGB 32bpp */ | ||
63 | writel(0x1c, fbi->regbase + WMT_GOVR_COLORSPACE); | ||
64 | writel(1, fbi->regbase + WMT_GOVR_COLORSPACE1); | ||
65 | |||
66 | /* Virtual buffer size */ | ||
67 | writel(info->var.xres, fbi->regbase + WMT_GOVR_XRES); | ||
68 | writel(info->var.xres_virtual, fbi->regbase + WMT_GOVR_XRES_VIRTUAL); | ||
69 | |||
70 | /* black magic ;) */ | ||
71 | writel(0xf, fbi->regbase + WMT_GOVR_FHI); | ||
72 | writel(4, fbi->regbase + WMT_GOVR_DVO_SET); | ||
73 | writel(1, fbi->regbase + WMT_GOVR_MIF_ENABLE); | ||
74 | writel(1, fbi->regbase + WMT_GOVR_REG_UPDATE); | ||
75 | |||
76 | return 0; | ||
77 | } | ||
78 | |||
79 | static int wm8505fb_set_timing(struct fb_info *info) | ||
80 | { | ||
81 | struct wm8505fb_info *fbi = to_wm8505fb_info(info); | ||
82 | |||
83 | int h_start = info->var.left_margin; | ||
84 | int h_end = h_start + info->var.xres; | ||
85 | int h_all = h_end + info->var.right_margin; | ||
86 | int h_sync = info->var.hsync_len; | ||
87 | |||
88 | int v_start = info->var.upper_margin; | ||
89 | int v_end = v_start + info->var.yres; | ||
90 | int v_all = v_end + info->var.lower_margin; | ||
91 | int v_sync = info->var.vsync_len; | ||
92 | |||
93 | writel(0, fbi->regbase + WMT_GOVR_TG); | ||
94 | |||
95 | writel(h_start, fbi->regbase + WMT_GOVR_TIMING_H_START); | ||
96 | writel(h_end, fbi->regbase + WMT_GOVR_TIMING_H_END); | ||
97 | writel(h_all, fbi->regbase + WMT_GOVR_TIMING_H_ALL); | ||
98 | writel(h_sync, fbi->regbase + WMT_GOVR_TIMING_H_SYNC); | ||
99 | |||
100 | writel(v_start, fbi->regbase + WMT_GOVR_TIMING_V_START); | ||
101 | writel(v_end, fbi->regbase + WMT_GOVR_TIMING_V_END); | ||
102 | writel(v_all, fbi->regbase + WMT_GOVR_TIMING_V_ALL); | ||
103 | writel(v_sync, fbi->regbase + WMT_GOVR_TIMING_V_SYNC); | ||
104 | |||
105 | writel(1, fbi->regbase + WMT_GOVR_TG); | ||
106 | |||
107 | return 0; | ||
108 | } | ||
109 | |||
110 | |||
111 | static int wm8505fb_set_par(struct fb_info *info) | ||
112 | { | ||
113 | struct wm8505fb_info *fbi = to_wm8505fb_info(info); | ||
114 | |||
115 | if (!fbi) | ||
116 | return -EINVAL; | ||
117 | |||
118 | if (info->var.bits_per_pixel == 32) { | ||
119 | info->var.red.offset = 16; | ||
120 | info->var.red.length = 8; | ||
121 | info->var.red.msb_right = 0; | ||
122 | info->var.green.offset = 8; | ||
123 | info->var.green.length = 8; | ||
124 | info->var.green.msb_right = 0; | ||
125 | info->var.blue.offset = 0; | ||
126 | info->var.blue.length = 8; | ||
127 | info->var.blue.msb_right = 0; | ||
128 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
129 | info->fix.line_length = info->var.xres_virtual << 2; | ||
130 | } | ||
131 | |||
132 | wm8505fb_set_timing(info); | ||
133 | |||
134 | writel(fbi->contrast<<16 | fbi->contrast<<8 | fbi->contrast, | ||
135 | fbi->regbase + WMT_GOVR_CONTRAST); | ||
136 | |||
137 | return 0; | ||
138 | } | ||
139 | |||
140 | static ssize_t contrast_show(struct device *dev, | ||
141 | struct device_attribute *attr, char *buf) | ||
142 | { | ||
143 | struct fb_info *info = dev_get_drvdata(dev); | ||
144 | struct wm8505fb_info *fbi = to_wm8505fb_info(info); | ||
145 | |||
146 | return sprintf(buf, "%d\n", fbi->contrast); | ||
147 | } | ||
148 | |||
149 | static ssize_t contrast_store(struct device *dev, | ||
150 | struct device_attribute *attr, | ||
151 | const char *buf, size_t count) | ||
152 | { | ||
153 | struct fb_info *info = dev_get_drvdata(dev); | ||
154 | struct wm8505fb_info *fbi = to_wm8505fb_info(info); | ||
155 | unsigned long tmp; | ||
156 | |||
157 | if (strict_strtoul(buf, 10, &tmp) || (tmp > 0xff)) | ||
158 | return -EINVAL; | ||
159 | fbi->contrast = tmp; | ||
160 | |||
161 | wm8505fb_set_par(info); | ||
162 | |||
163 | return count; | ||
164 | } | ||
165 | |||
166 | static DEVICE_ATTR(contrast, 0644, contrast_show, contrast_store); | ||
167 | |||
168 | static inline u_int chan_to_field(u_int chan, struct fb_bitfield *bf) | ||
169 | { | ||
170 | chan &= 0xffff; | ||
171 | chan >>= 16 - bf->length; | ||
172 | return chan << bf->offset; | ||
173 | } | ||
174 | |||
175 | static int wm8505fb_setcolreg(unsigned regno, unsigned red, unsigned green, | ||
176 | unsigned blue, unsigned transp, | ||
177 | struct fb_info *info) { | ||
178 | struct wm8505fb_info *fbi = to_wm8505fb_info(info); | ||
179 | int ret = 1; | ||
180 | unsigned int val; | ||
181 | if (regno >= 256) | ||
182 | return -EINVAL; | ||
183 | |||
184 | if (info->var.grayscale) | ||
185 | red = green = blue = | ||
186 | (19595 * red + 38470 * green + 7471 * blue) >> 16; | ||
187 | |||
188 | switch (fbi->fb.fix.visual) { | ||
189 | case FB_VISUAL_TRUECOLOR: | ||
190 | if (regno < 16) { | ||
191 | u32 *pal = info->pseudo_palette; | ||
192 | |||
193 | val = chan_to_field(red, &fbi->fb.var.red); | ||
194 | val |= chan_to_field(green, &fbi->fb.var.green); | ||
195 | val |= chan_to_field(blue, &fbi->fb.var.blue); | ||
196 | |||
197 | pal[regno] = val; | ||
198 | ret = 0; | ||
199 | } | ||
200 | break; | ||
201 | } | ||
202 | |||
203 | return ret; | ||
204 | } | ||
205 | |||
206 | static int wm8505fb_pan_display(struct fb_var_screeninfo *var, | ||
207 | struct fb_info *info) | ||
208 | { | ||
209 | struct wm8505fb_info *fbi = to_wm8505fb_info(info); | ||
210 | |||
211 | writel(var->xoffset, fbi->regbase + WMT_GOVR_XPAN); | ||
212 | writel(var->yoffset, fbi->regbase + WMT_GOVR_YPAN); | ||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | static int wm8505fb_blank(int blank, struct fb_info *info) | ||
217 | { | ||
218 | struct wm8505fb_info *fbi = to_wm8505fb_info(info); | ||
219 | |||
220 | switch (blank) { | ||
221 | case FB_BLANK_UNBLANK: | ||
222 | wm8505fb_set_timing(info); | ||
223 | break; | ||
224 | default: | ||
225 | writel(0, fbi->regbase + WMT_GOVR_TIMING_V_SYNC); | ||
226 | break; | ||
227 | } | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | static struct fb_ops wm8505fb_ops = { | ||
233 | .owner = THIS_MODULE, | ||
234 | .fb_set_par = wm8505fb_set_par, | ||
235 | .fb_setcolreg = wm8505fb_setcolreg, | ||
236 | .fb_fillrect = wmt_ge_fillrect, | ||
237 | .fb_copyarea = wmt_ge_copyarea, | ||
238 | .fb_imageblit = sys_imageblit, | ||
239 | .fb_sync = wmt_ge_sync, | ||
240 | .fb_pan_display = wm8505fb_pan_display, | ||
241 | .fb_blank = wm8505fb_blank, | ||
242 | }; | ||
243 | |||
244 | static int __devinit wm8505fb_probe(struct platform_device *pdev) | ||
245 | { | ||
246 | struct wm8505fb_info *fbi; | ||
247 | struct resource *res; | ||
248 | void *addr; | ||
249 | struct vt8500fb_platform_data *pdata; | ||
250 | int ret; | ||
251 | |||
252 | pdata = pdev->dev.platform_data; | ||
253 | |||
254 | ret = -ENOMEM; | ||
255 | fbi = NULL; | ||
256 | |||
257 | fbi = kzalloc(sizeof(struct wm8505fb_info) + sizeof(u32) * 16, | ||
258 | GFP_KERNEL); | ||
259 | if (!fbi) { | ||
260 | dev_err(&pdev->dev, "Failed to initialize framebuffer device\n"); | ||
261 | ret = -ENOMEM; | ||
262 | goto failed; | ||
263 | } | ||
264 | |||
265 | strcpy(fbi->fb.fix.id, DRIVER_NAME); | ||
266 | |||
267 | fbi->fb.fix.type = FB_TYPE_PACKED_PIXELS; | ||
268 | fbi->fb.fix.xpanstep = 1; | ||
269 | fbi->fb.fix.ypanstep = 1; | ||
270 | fbi->fb.fix.ywrapstep = 0; | ||
271 | fbi->fb.fix.accel = FB_ACCEL_NONE; | ||
272 | |||
273 | fbi->fb.fbops = &wm8505fb_ops; | ||
274 | fbi->fb.flags = FBINFO_DEFAULT | ||
275 | | FBINFO_HWACCEL_COPYAREA | ||
276 | | FBINFO_HWACCEL_FILLRECT | ||
277 | | FBINFO_HWACCEL_XPAN | ||
278 | | FBINFO_HWACCEL_YPAN | ||
279 | | FBINFO_VIRTFB | ||
280 | | FBINFO_PARTIAL_PAN_OK; | ||
281 | fbi->fb.node = -1; | ||
282 | |||
283 | addr = fbi; | ||
284 | addr = addr + sizeof(struct wm8505fb_info); | ||
285 | fbi->fb.pseudo_palette = addr; | ||
286 | |||
287 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
288 | if (res == NULL) { | ||
289 | dev_err(&pdev->dev, "no I/O memory resource defined\n"); | ||
290 | ret = -ENODEV; | ||
291 | goto failed_fbi; | ||
292 | } | ||
293 | |||
294 | res = request_mem_region(res->start, resource_size(res), DRIVER_NAME); | ||
295 | if (res == NULL) { | ||
296 | dev_err(&pdev->dev, "failed to request I/O memory\n"); | ||
297 | ret = -EBUSY; | ||
298 | goto failed_fbi; | ||
299 | } | ||
300 | |||
301 | fbi->regbase = ioremap(res->start, resource_size(res)); | ||
302 | if (fbi->regbase == NULL) { | ||
303 | dev_err(&pdev->dev, "failed to map I/O memory\n"); | ||
304 | ret = -EBUSY; | ||
305 | goto failed_free_res; | ||
306 | } | ||
307 | |||
308 | fb_videomode_to_var(&fbi->fb.var, &pdata->mode); | ||
309 | |||
310 | fbi->fb.var.nonstd = 0; | ||
311 | fbi->fb.var.activate = FB_ACTIVATE_NOW; | ||
312 | |||
313 | fbi->fb.var.height = -1; | ||
314 | fbi->fb.var.width = -1; | ||
315 | fbi->fb.var.xres_virtual = pdata->xres_virtual; | ||
316 | fbi->fb.var.yres_virtual = pdata->yres_virtual; | ||
317 | fbi->fb.var.bits_per_pixel = pdata->bpp; | ||
318 | |||
319 | fbi->fb.fix.smem_start = pdata->video_mem_phys; | ||
320 | fbi->fb.fix.smem_len = pdata->video_mem_len; | ||
321 | fbi->fb.screen_base = pdata->video_mem_virt; | ||
322 | fbi->fb.screen_size = pdata->video_mem_len; | ||
323 | |||
324 | if (fb_alloc_cmap(&fbi->fb.cmap, 256, 0) < 0) { | ||
325 | dev_err(&pdev->dev, "Failed to allocate color map\n"); | ||
326 | ret = -ENOMEM; | ||
327 | goto failed_free_io; | ||
328 | } | ||
329 | |||
330 | wm8505fb_init_hw(&fbi->fb); | ||
331 | |||
332 | fbi->contrast = 0x80; | ||
333 | ret = wm8505fb_set_par(&fbi->fb); | ||
334 | if (ret) { | ||
335 | dev_err(&pdev->dev, "Failed to set parameters\n"); | ||
336 | goto failed_free_cmap; | ||
337 | } | ||
338 | |||
339 | platform_set_drvdata(pdev, fbi); | ||
340 | |||
341 | ret = register_framebuffer(&fbi->fb); | ||
342 | if (ret < 0) { | ||
343 | dev_err(&pdev->dev, | ||
344 | "Failed to register framebuffer device: %d\n", ret); | ||
345 | goto failed_free_cmap; | ||
346 | } | ||
347 | |||
348 | ret = device_create_file(&pdev->dev, &dev_attr_contrast); | ||
349 | if (ret < 0) { | ||
350 | printk(KERN_WARNING "fb%d: failed to register attributes (%d)\n", | ||
351 | fbi->fb.node, ret); | ||
352 | } | ||
353 | |||
354 | printk(KERN_INFO "fb%d: %s frame buffer at 0x%lx-0x%lx\n", | ||
355 | fbi->fb.node, fbi->fb.fix.id, fbi->fb.fix.smem_start, | ||
356 | fbi->fb.fix.smem_start + fbi->fb.fix.smem_len - 1); | ||
357 | |||
358 | return 0; | ||
359 | |||
360 | failed_free_cmap: | ||
361 | if (fbi->fb.cmap.len) | ||
362 | fb_dealloc_cmap(&fbi->fb.cmap); | ||
363 | failed_free_io: | ||
364 | iounmap(fbi->regbase); | ||
365 | failed_free_res: | ||
366 | release_mem_region(res->start, resource_size(res)); | ||
367 | failed_fbi: | ||
368 | platform_set_drvdata(pdev, NULL); | ||
369 | kfree(fbi); | ||
370 | failed: | ||
371 | return ret; | ||
372 | } | ||
373 | |||
374 | static int __devexit wm8505fb_remove(struct platform_device *pdev) | ||
375 | { | ||
376 | struct wm8505fb_info *fbi = platform_get_drvdata(pdev); | ||
377 | struct resource *res; | ||
378 | |||
379 | device_remove_file(&pdev->dev, &dev_attr_contrast); | ||
380 | |||
381 | unregister_framebuffer(&fbi->fb); | ||
382 | |||
383 | writel(0, fbi->regbase); | ||
384 | |||
385 | if (fbi->fb.cmap.len) | ||
386 | fb_dealloc_cmap(&fbi->fb.cmap); | ||
387 | |||
388 | iounmap(fbi->regbase); | ||
389 | |||
390 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
391 | release_mem_region(res->start, resource_size(res)); | ||
392 | |||
393 | kfree(fbi); | ||
394 | |||
395 | return 0; | ||
396 | } | ||
397 | |||
398 | static struct platform_driver wm8505fb_driver = { | ||
399 | .probe = wm8505fb_probe, | ||
400 | .remove = __devexit_p(wm8505fb_remove), | ||
401 | .driver = { | ||
402 | .owner = THIS_MODULE, | ||
403 | .name = DRIVER_NAME, | ||
404 | }, | ||
405 | }; | ||
406 | |||
407 | static int __init wm8505fb_init(void) | ||
408 | { | ||
409 | return platform_driver_register(&wm8505fb_driver); | ||
410 | } | ||
411 | |||
412 | static void __exit wm8505fb_exit(void) | ||
413 | { | ||
414 | platform_driver_unregister(&wm8505fb_driver); | ||
415 | } | ||
416 | |||
417 | module_init(wm8505fb_init); | ||
418 | module_exit(wm8505fb_exit); | ||
419 | |||
420 | MODULE_AUTHOR("Ed Spiridonov <edo.rus@gmail.com>"); | ||
421 | MODULE_DESCRIPTION("Framebuffer driver for WMT WM8505"); | ||
422 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/wm8505fb_regs.h b/drivers/video/wm8505fb_regs.h new file mode 100644 index 000000000000..4dd41668c6d1 --- /dev/null +++ b/drivers/video/wm8505fb_regs.h | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * GOVR registers list for WM8505 chips | ||
3 | * | ||
4 | * Copyright (C) 2010 Ed Spiridonov <edo.rus@gmail.com> | ||
5 | * Based on VIA/WonderMedia wm8510-govrh-reg.h | ||
6 | * http://github.com/projectgus/kernel_wm8505/blob/wm8505_2.6.29/ | ||
7 | * drivers/video/wmt/register/wm8510/wm8510-govrh-reg.h | ||
8 | * | ||
9 | * This software is licensed under the terms of the GNU General Public | ||
10 | * License version 2, as published by the Free Software Foundation, and | ||
11 | * may be copied, distributed, and modified under those terms. | ||
12 | * | ||
13 | * This program is distributed in the hope that it will be useful, | ||
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | * GNU General Public License for more details. | ||
17 | */ | ||
18 | |||
19 | #ifndef _WM8505FB_REGS_H | ||
20 | #define _WM8505FB_REGS_H | ||
21 | |||
22 | /* | ||
23 | * Color space select register, default value 0x1c | ||
24 | * BIT0 GOVRH_DVO_YUV2RGB_ENABLE | ||
25 | * BIT1 GOVRH_VGA_YUV2RGB_ENABLE | ||
26 | * BIT2 GOVRH_RGB_MODE | ||
27 | * BIT3 GOVRH_DAC_CLKINV | ||
28 | * BIT4 GOVRH_BLANK_ZERO | ||
29 | */ | ||
30 | #define WMT_GOVR_COLORSPACE 0x1e4 | ||
31 | /* | ||
32 | * Another colorspace select register, default value 1 | ||
33 | * BIT0 GOVRH_DVO_RGB | ||
34 | * BIT1 GOVRH_DVO_YUV422 | ||
35 | */ | ||
36 | #define WMT_GOVR_COLORSPACE1 0x30 | ||
37 | |||
38 | #define WMT_GOVR_CONTRAST 0x1b8 | ||
39 | #define WMT_GOVR_BRGHTNESS 0x1bc /* incompatible with RGB? */ | ||
40 | |||
41 | /* Framubeffer address */ | ||
42 | #define WMT_GOVR_FBADDR 0x90 | ||
43 | #define WMT_GOVR_FBADDR1 0x94 /* UV offset in YUV mode */ | ||
44 | |||
45 | /* Offset of visible window */ | ||
46 | #define WMT_GOVR_XPAN 0xa4 | ||
47 | #define WMT_GOVR_YPAN 0xa0 | ||
48 | |||
49 | #define WMT_GOVR_XRES 0x98 | ||
50 | #define WMT_GOVR_XRES_VIRTUAL 0x9c | ||
51 | |||
52 | #define WMT_GOVR_MIF_ENABLE 0x80 | ||
53 | #define WMT_GOVR_FHI 0xa8 | ||
54 | #define WMT_GOVR_REG_UPDATE 0xe4 | ||
55 | |||
56 | /* | ||
57 | * BIT0 GOVRH_DVO_OUTWIDTH | ||
58 | * BIT1 GOVRH_DVO_SYNC_POLAR | ||
59 | * BIT2 GOVRH_DVO_ENABLE | ||
60 | */ | ||
61 | #define WMT_GOVR_DVO_SET 0x148 | ||
62 | |||
63 | /* Timing generator? */ | ||
64 | #define WMT_GOVR_TG 0x100 | ||
65 | |||
66 | /* Timings */ | ||
67 | #define WMT_GOVR_TIMING_H_ALL 0x108 | ||
68 | #define WMT_GOVR_TIMING_V_ALL 0x10c | ||
69 | #define WMT_GOVR_TIMING_V_START 0x110 | ||
70 | #define WMT_GOVR_TIMING_V_END 0x114 | ||
71 | #define WMT_GOVR_TIMING_H_START 0x118 | ||
72 | #define WMT_GOVR_TIMING_H_END 0x11c | ||
73 | #define WMT_GOVR_TIMING_V_SYNC 0x128 | ||
74 | #define WMT_GOVR_TIMING_H_SYNC 0x12c | ||
75 | |||
76 | #endif /* _WM8505FB_REGS_H */ | ||
diff --git a/drivers/video/wmt_ge_rops.c b/drivers/video/wmt_ge_rops.c new file mode 100644 index 000000000000..45832b7ef7d2 --- /dev/null +++ b/drivers/video/wmt_ge_rops.c | |||
@@ -0,0 +1,186 @@ | |||
1 | /* | ||
2 | * linux/drivers/video/wmt_ge_rops.c | ||
3 | * | ||
4 | * Accelerators for raster operations using WonderMedia Graphics Engine | ||
5 | * | ||
6 | * Copyright (C) 2010 Alexey Charkov <alchark@gmail.com> | ||
7 | * | ||
8 | * This software is licensed under the terms of the GNU General Public | ||
9 | * License version 2, as published by the Free Software Foundation, and | ||
10 | * may be copied, distributed, and modified under those terms. | ||
11 | * | ||
12 | * This program is distributed in the hope that it will be useful, | ||
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
15 | * GNU General Public License for more details. | ||
16 | */ | ||
17 | |||
18 | #include <linux/module.h> | ||
19 | #include <linux/fb.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include "fb_draw.h" | ||
22 | |||
23 | #define GE_COMMAND_OFF 0x00 | ||
24 | #define GE_DEPTH_OFF 0x04 | ||
25 | #define GE_HIGHCOLOR_OFF 0x08 | ||
26 | #define GE_ROPCODE_OFF 0x14 | ||
27 | #define GE_FIRE_OFF 0x18 | ||
28 | #define GE_SRCBASE_OFF 0x20 | ||
29 | #define GE_SRCDISPW_OFF 0x24 | ||
30 | #define GE_SRCDISPH_OFF 0x28 | ||
31 | #define GE_SRCAREAX_OFF 0x2c | ||
32 | #define GE_SRCAREAY_OFF 0x30 | ||
33 | #define GE_SRCAREAW_OFF 0x34 | ||
34 | #define GE_SRCAREAH_OFF 0x38 | ||
35 | #define GE_DESTBASE_OFF 0x3c | ||
36 | #define GE_DESTDISPW_OFF 0x40 | ||
37 | #define GE_DESTDISPH_OFF 0x44 | ||
38 | #define GE_DESTAREAX_OFF 0x48 | ||
39 | #define GE_DESTAREAY_OFF 0x4c | ||
40 | #define GE_DESTAREAW_OFF 0x50 | ||
41 | #define GE_DESTAREAH_OFF 0x54 | ||
42 | #define GE_PAT0C_OFF 0x88 /* Pattern 0 color */ | ||
43 | #define GE_ENABLE_OFF 0xec | ||
44 | #define GE_INTEN_OFF 0xf0 | ||
45 | #define GE_STATUS_OFF 0xf8 | ||
46 | |||
47 | static void __iomem *regbase; | ||
48 | |||
49 | void wmt_ge_fillrect(struct fb_info *p, const struct fb_fillrect *rect) | ||
50 | { | ||
51 | unsigned long fg, pat; | ||
52 | |||
53 | if (p->state != FBINFO_STATE_RUNNING) | ||
54 | return; | ||
55 | |||
56 | if (p->fix.visual == FB_VISUAL_TRUECOLOR || | ||
57 | p->fix.visual == FB_VISUAL_DIRECTCOLOR) | ||
58 | fg = ((u32 *) (p->pseudo_palette))[rect->color]; | ||
59 | else | ||
60 | fg = rect->color; | ||
61 | |||
62 | pat = pixel_to_pat(p->var.bits_per_pixel, fg); | ||
63 | |||
64 | if (p->fbops->fb_sync) | ||
65 | p->fbops->fb_sync(p); | ||
66 | |||
67 | writel(p->var.bits_per_pixel == 32 ? 3 : | ||
68 | (p->var.bits_per_pixel == 8 ? 0 : 1), regbase + GE_DEPTH_OFF); | ||
69 | writel(p->var.bits_per_pixel == 15 ? 1 : 0, regbase + GE_HIGHCOLOR_OFF); | ||
70 | writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF); | ||
71 | writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF); | ||
72 | writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF); | ||
73 | writel(rect->dx, regbase + GE_DESTAREAX_OFF); | ||
74 | writel(rect->dy, regbase + GE_DESTAREAY_OFF); | ||
75 | writel(rect->width - 1, regbase + GE_DESTAREAW_OFF); | ||
76 | writel(rect->height - 1, regbase + GE_DESTAREAH_OFF); | ||
77 | |||
78 | writel(pat, regbase + GE_PAT0C_OFF); | ||
79 | writel(1, regbase + GE_COMMAND_OFF); | ||
80 | writel(rect->rop == ROP_XOR ? 0x5a : 0xf0, regbase + GE_ROPCODE_OFF); | ||
81 | writel(1, regbase + GE_FIRE_OFF); | ||
82 | } | ||
83 | EXPORT_SYMBOL_GPL(wmt_ge_fillrect); | ||
84 | |||
85 | void wmt_ge_copyarea(struct fb_info *p, const struct fb_copyarea *area) | ||
86 | { | ||
87 | if (p->state != FBINFO_STATE_RUNNING) | ||
88 | return; | ||
89 | |||
90 | if (p->fbops->fb_sync) | ||
91 | p->fbops->fb_sync(p); | ||
92 | |||
93 | writel(p->var.bits_per_pixel > 16 ? 3 : | ||
94 | (p->var.bits_per_pixel > 8 ? 1 : 0), regbase + GE_DEPTH_OFF); | ||
95 | |||
96 | writel(p->fix.smem_start, regbase + GE_SRCBASE_OFF); | ||
97 | writel(p->var.xres_virtual - 1, regbase + GE_SRCDISPW_OFF); | ||
98 | writel(p->var.yres_virtual - 1, regbase + GE_SRCDISPH_OFF); | ||
99 | writel(area->sx, regbase + GE_SRCAREAX_OFF); | ||
100 | writel(area->sy, regbase + GE_SRCAREAY_OFF); | ||
101 | writel(area->width - 1, regbase + GE_SRCAREAW_OFF); | ||
102 | writel(area->height - 1, regbase + GE_SRCAREAH_OFF); | ||
103 | |||
104 | writel(p->fix.smem_start, regbase + GE_DESTBASE_OFF); | ||
105 | writel(p->var.xres_virtual - 1, regbase + GE_DESTDISPW_OFF); | ||
106 | writel(p->var.yres_virtual - 1, regbase + GE_DESTDISPH_OFF); | ||
107 | writel(area->dx, regbase + GE_DESTAREAX_OFF); | ||
108 | writel(area->dy, regbase + GE_DESTAREAY_OFF); | ||
109 | writel(area->width - 1, regbase + GE_DESTAREAW_OFF); | ||
110 | writel(area->height - 1, regbase + GE_DESTAREAH_OFF); | ||
111 | |||
112 | writel(0xcc, regbase + GE_ROPCODE_OFF); | ||
113 | writel(1, regbase + GE_COMMAND_OFF); | ||
114 | writel(1, regbase + GE_FIRE_OFF); | ||
115 | } | ||
116 | EXPORT_SYMBOL_GPL(wmt_ge_copyarea); | ||
117 | |||
118 | int wmt_ge_sync(struct fb_info *p) | ||
119 | { | ||
120 | int loops = 5000000; | ||
121 | while ((readl(regbase + GE_STATUS_OFF) & 4) && --loops) | ||
122 | cpu_relax(); | ||
123 | return loops > 0 ? 0 : -EBUSY; | ||
124 | } | ||
125 | EXPORT_SYMBOL_GPL(wmt_ge_sync); | ||
126 | |||
127 | static int __devinit wmt_ge_rops_probe(struct platform_device *pdev) | ||
128 | { | ||
129 | struct resource *res; | ||
130 | |||
131 | res = platform_get_resource(pdev, IORESOURCE_MEM, 0); | ||
132 | if (res == NULL) { | ||
133 | dev_err(&pdev->dev, "no I/O memory resource defined\n"); | ||
134 | return -ENODEV; | ||
135 | } | ||
136 | |||
137 | /* Only one ROP engine is presently supported. */ | ||
138 | if (unlikely(regbase)) { | ||
139 | WARN_ON(1); | ||
140 | return -EBUSY; | ||
141 | } | ||
142 | |||
143 | regbase = ioremap(res->start, resource_size(res)); | ||
144 | if (regbase == NULL) { | ||
145 | dev_err(&pdev->dev, "failed to map I/O memory\n"); | ||
146 | return -EBUSY; | ||
147 | } | ||
148 | |||
149 | writel(1, regbase + GE_ENABLE_OFF); | ||
150 | printk(KERN_INFO "Enabled support for WMT GE raster acceleration\n"); | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static int __devexit wmt_ge_rops_remove(struct platform_device *pdev) | ||
156 | { | ||
157 | iounmap(regbase); | ||
158 | return 0; | ||
159 | } | ||
160 | |||
161 | static struct platform_driver wmt_ge_rops_driver = { | ||
162 | .probe = wmt_ge_rops_probe, | ||
163 | .remove = __devexit_p(wmt_ge_rops_remove), | ||
164 | .driver = { | ||
165 | .owner = THIS_MODULE, | ||
166 | .name = "wmt_ge_rops", | ||
167 | }, | ||
168 | }; | ||
169 | |||
170 | static int __init wmt_ge_rops_init(void) | ||
171 | { | ||
172 | return platform_driver_register(&wmt_ge_rops_driver); | ||
173 | } | ||
174 | |||
175 | static void __exit wmt_ge_rops_exit(void) | ||
176 | { | ||
177 | platform_driver_unregister(&wmt_ge_rops_driver); | ||
178 | } | ||
179 | |||
180 | module_init(wmt_ge_rops_init); | ||
181 | module_exit(wmt_ge_rops_exit); | ||
182 | |||
183 | MODULE_AUTHOR("Alexey Charkov <alchark@gmail.com"); | ||
184 | MODULE_DESCRIPTION("Accelerators for raster operations using " | ||
185 | "WonderMedia Graphics Engine"); | ||
186 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/video/wmt_ge_rops.h b/drivers/video/wmt_ge_rops.h new file mode 100644 index 000000000000..87380751a443 --- /dev/null +++ b/drivers/video/wmt_ge_rops.h | |||
@@ -0,0 +1,5 @@ | |||
1 | extern void wmt_ge_fillrect(struct fb_info *info, | ||
2 | const struct fb_fillrect *rect); | ||
3 | extern void wmt_ge_copyarea(struct fb_info *info, | ||
4 | const struct fb_copyarea *area); | ||
5 | extern int wmt_ge_sync(struct fb_info *info); | ||
diff --git a/include/linux/fb.h b/include/linux/fb.h index d1631d37e9e0..68ba85a00c06 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h | |||
@@ -1092,6 +1092,8 @@ extern int fb_parse_edid(unsigned char *edid, struct fb_var_screeninfo *var); | |||
1092 | extern const unsigned char *fb_firmware_edid(struct device *device); | 1092 | extern const unsigned char *fb_firmware_edid(struct device *device); |
1093 | extern void fb_edid_to_monspecs(unsigned char *edid, | 1093 | extern void fb_edid_to_monspecs(unsigned char *edid, |
1094 | struct fb_monspecs *specs); | 1094 | struct fb_monspecs *specs); |
1095 | extern void fb_edid_add_monspecs(unsigned char *edid, | ||
1096 | struct fb_monspecs *specs); | ||
1095 | extern void fb_destroy_modedb(struct fb_videomode *modedb); | 1097 | extern void fb_destroy_modedb(struct fb_videomode *modedb); |
1096 | extern int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb); | 1098 | extern int fb_find_mode_cvt(struct fb_videomode *mode, int margins, int rb); |
1097 | extern unsigned char *fb_ddc_read(struct i2c_adapter *adapter); | 1099 | extern unsigned char *fb_ddc_read(struct i2c_adapter *adapter); |
@@ -1150,6 +1152,7 @@ struct fb_videomode { | |||
1150 | 1152 | ||
1151 | extern const char *fb_mode_option; | 1153 | extern const char *fb_mode_option; |
1152 | extern const struct fb_videomode vesa_modes[]; | 1154 | extern const struct fb_videomode vesa_modes[]; |
1155 | extern const struct fb_videomode cea_modes[64]; | ||
1153 | 1156 | ||
1154 | struct fb_modelist { | 1157 | struct fb_modelist { |
1155 | struct list_head list; | 1158 | struct list_head list; |
diff --git a/include/linux/via-core.h b/include/linux/via-core.h index 38bffd8ccca5..9c21cdf3e3b3 100644 --- a/include/linux/via-core.h +++ b/include/linux/via-core.h | |||
@@ -60,6 +60,21 @@ struct via_port_cfg { | |||
60 | }; | 60 | }; |
61 | 61 | ||
62 | /* | 62 | /* |
63 | * Allow subdevs to register suspend/resume hooks. | ||
64 | */ | ||
65 | #ifdef CONFIG_PM | ||
66 | struct viafb_pm_hooks { | ||
67 | struct list_head list; | ||
68 | int (*suspend)(void *private); | ||
69 | int (*resume)(void *private); | ||
70 | void *private; | ||
71 | }; | ||
72 | |||
73 | void viafb_pm_register(struct viafb_pm_hooks *hooks); | ||
74 | void viafb_pm_unregister(struct viafb_pm_hooks *hooks); | ||
75 | #endif /* CONFIG_PM */ | ||
76 | |||
77 | /* | ||
63 | * This is the global viafb "device" containing stuff needed by | 78 | * This is the global viafb "device" containing stuff needed by |
64 | * all subdevs. | 79 | * all subdevs. |
65 | */ | 80 | */ |
diff --git a/include/video/s1d13xxxfb.h b/include/video/s1d13xxxfb.h index f0736cff2ca3..55f534491a3d 100644 --- a/include/video/s1d13xxxfb.h +++ b/include/video/s1d13xxxfb.h | |||
@@ -136,12 +136,6 @@ | |||
136 | #define S1DREG_DELAYOFF 0xFFFE | 136 | #define S1DREG_DELAYOFF 0xFFFE |
137 | #define S1DREG_DELAYON 0xFFFF | 137 | #define S1DREG_DELAYON 0xFFFF |
138 | 138 | ||
139 | #define BBLT_FIFO_EMPTY 0x00 | ||
140 | #define BBLT_FIFO_NOT_EMPTY 0x40 | ||
141 | #define BBLT_FIFO_NOT_FULL 0x30 | ||
142 | #define BBLT_FIFO_HALF_FULL 0x20 | ||
143 | #define BBLT_FIFO_FULL 0x10 | ||
144 | |||
145 | #define BBLT_SOLID_FILL 0x0c | 139 | #define BBLT_SOLID_FILL 0x0c |
146 | 140 | ||
147 | 141 | ||
diff --git a/include/video/sh_mobile_hdmi.h b/include/video/sh_mobile_hdmi.h index 1e1aa54ab2e4..b56932927d0a 100644 --- a/include/video/sh_mobile_hdmi.h +++ b/include/video/sh_mobile_hdmi.h | |||
@@ -13,6 +13,7 @@ | |||
13 | 13 | ||
14 | struct sh_mobile_lcdc_chan_cfg; | 14 | struct sh_mobile_lcdc_chan_cfg; |
15 | struct device; | 15 | struct device; |
16 | struct clk; | ||
16 | 17 | ||
17 | /* | 18 | /* |
18 | * flags format | 19 | * flags format |
@@ -33,6 +34,8 @@ struct sh_mobile_hdmi_info { | |||
33 | struct sh_mobile_lcdc_chan_cfg *lcd_chan; | 34 | struct sh_mobile_lcdc_chan_cfg *lcd_chan; |
34 | struct device *lcd_dev; | 35 | struct device *lcd_dev; |
35 | unsigned int flags; | 36 | unsigned int flags; |
37 | long (*clk_optimize_parent)(unsigned long target, unsigned long *best_freq, | ||
38 | unsigned long *parent_freq); | ||
36 | }; | 39 | }; |
37 | 40 | ||
38 | #endif | 41 | #endif |