diff options
author | Dmitry Baryshkov <dbaryshkov@gmail.com> | 2008-10-16 01:03:55 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2008-10-16 14:21:45 -0400 |
commit | b53cde3557b8f97e6a635782875d442551a89bf1 (patch) | |
tree | 4ce92a40042262fb8bdba2275c4292dcc0275ac6 /drivers/video | |
parent | d5ca900603283ccb41a9c0c831dd179938d137b2 (diff) |
fbdev: add new TMIO framebuffer driver
Add driver for TMIO framebuffer cells as found e.g. in Toshiba TC6393XB
chips.
Signed-off-by: Dmitry Baryshkov <dbaryshkov@gmail.com>
Cc: Ian Molton <spyro@f2s.com>
Acked-by: Samuel Ortiz <sameo@openedhand.com>
Acked-by: Krzysztof Helt <krzysztof.h1@wp.pl>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/video')
-rw-r--r-- | drivers/video/Kconfig | 22 | ||||
-rw-r--r-- | drivers/video/Makefile | 1 | ||||
-rw-r--r-- | drivers/video/tmiofb.c | 1050 |
3 files changed, 1073 insertions, 0 deletions
diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig index 6210755cf6ab..0f13448c6f79 100644 --- a/drivers/video/Kconfig +++ b/drivers/video/Kconfig | |||
@@ -1896,6 +1896,28 @@ config FB_SH_MOBILE_LCDC | |||
1896 | ---help--- | 1896 | ---help--- |
1897 | Frame buffer driver for the on-chip SH-Mobile LCD controller. | 1897 | Frame buffer driver for the on-chip SH-Mobile LCD controller. |
1898 | 1898 | ||
1899 | config FB_TMIO | ||
1900 | tristate "Toshiba Mobile IO FrameBuffer support" | ||
1901 | depends on FB && MFD_CORE | ||
1902 | select FB_CFB_FILLRECT | ||
1903 | select FB_CFB_COPYAREA | ||
1904 | select FB_CFB_IMAGEBLIT | ||
1905 | ---help--- | ||
1906 | Frame buffer driver for the Toshiba Mobile IO integrated as found | ||
1907 | on the Sharp SL-6000 series | ||
1908 | |||
1909 | This driver is also available as a module ( = code which can be | ||
1910 | inserted and removed from the running kernel whenever you want). The | ||
1911 | module will be called tmiofb. If you want to compile it as a module, | ||
1912 | say M here and read <file:Documentation/kbuild/modules.txt>. | ||
1913 | |||
1914 | If unsure, say N. | ||
1915 | |||
1916 | config FB_TMIO_ACCELL | ||
1917 | bool "tmiofb acceleration" | ||
1918 | depends on FB_TMIO | ||
1919 | default y | ||
1920 | |||
1899 | config FB_S3C2410 | 1921 | config FB_S3C2410 |
1900 | tristate "S3C2410 LCD framebuffer support" | 1922 | tristate "S3C2410 LCD framebuffer support" |
1901 | depends on FB && ARCH_S3C2410 | 1923 | depends on FB && ARCH_S3C2410 |
diff --git a/drivers/video/Makefile b/drivers/video/Makefile index 2bc94d8eb3c8..248bddc8d0b0 100644 --- a/drivers/video/Makefile +++ b/drivers/video/Makefile | |||
@@ -98,6 +98,7 @@ obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o | |||
98 | obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o | 98 | obj-$(CONFIG_FB_ASILIANT) += asiliantfb.o |
99 | obj-$(CONFIG_FB_PXA) += pxafb.o | 99 | obj-$(CONFIG_FB_PXA) += pxafb.o |
100 | obj-$(CONFIG_FB_W100) += w100fb.o | 100 | obj-$(CONFIG_FB_W100) += w100fb.o |
101 | obj-$(CONFIG_FB_TMIO) += tmiofb.o | ||
101 | obj-$(CONFIG_FB_AU1100) += au1100fb.o | 102 | obj-$(CONFIG_FB_AU1100) += au1100fb.o |
102 | obj-$(CONFIG_FB_AU1200) += au1200fb.o | 103 | obj-$(CONFIG_FB_AU1200) += au1200fb.o |
103 | obj-$(CONFIG_FB_PMAG_AA) += pmag-aa-fb.o | 104 | obj-$(CONFIG_FB_PMAG_AA) += pmag-aa-fb.o |
diff --git a/drivers/video/tmiofb.c b/drivers/video/tmiofb.c new file mode 100644 index 000000000000..2a380011e9ba --- /dev/null +++ b/drivers/video/tmiofb.c | |||
@@ -0,0 +1,1050 @@ | |||
1 | /* | ||
2 | * Frame Buffer Device for Toshiba Mobile IO(TMIO) controller | ||
3 | * | ||
4 | * Copyright(C) 2005-2006 Chris Humbert | ||
5 | * Copyright(C) 2005 Dirk Opfer | ||
6 | * Copytight(C) 2007,2008 Dmitry Baryshkov | ||
7 | * | ||
8 | * Based on: | ||
9 | * drivers/video/w100fb.c | ||
10 | * code written by Sharp/Lineo for 2.4 kernels | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License version 2 | ||
14 | * as published by the Free Software Foundation; | ||
15 | * | ||
16 | * This program is distributed in the hope that it will be useful, | ||
17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
19 | * GNU General Public License for more details. | ||
20 | */ | ||
21 | |||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/platform_device.h> | ||
25 | #include <linux/fb.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/delay.h> | ||
28 | /* Why should fb driver call console functions? because acquire_console_sem() */ | ||
29 | #include <linux/console.h> | ||
30 | #include <linux/mfd/core.h> | ||
31 | #include <linux/mfd/tmio.h> | ||
32 | #include <linux/uaccess.h> | ||
33 | |||
34 | /* | ||
35 | * accelerator commands | ||
36 | */ | ||
37 | #define TMIOFB_ACC_CSADR(x) (0x00000000 | ((x) & 0x001ffffe)) | ||
38 | #define TMIOFB_ACC_CHPIX(x) (0x01000000 | ((x) & 0x000003ff)) | ||
39 | #define TMIOFB_ACC_CVPIX(x) (0x02000000 | ((x) & 0x000003ff)) | ||
40 | #define TMIOFB_ACC_PSADR(x) (0x03000000 | ((x) & 0x00fffffe)) | ||
41 | #define TMIOFB_ACC_PHPIX(x) (0x04000000 | ((x) & 0x000003ff)) | ||
42 | #define TMIOFB_ACC_PVPIX(x) (0x05000000 | ((x) & 0x000003ff)) | ||
43 | #define TMIOFB_ACC_PHOFS(x) (0x06000000 | ((x) & 0x000003ff)) | ||
44 | #define TMIOFB_ACC_PVOFS(x) (0x07000000 | ((x) & 0x000003ff)) | ||
45 | #define TMIOFB_ACC_POADR(x) (0x08000000 | ((x) & 0x00fffffe)) | ||
46 | #define TMIOFB_ACC_RSTR(x) (0x09000000 | ((x) & 0x000000ff)) | ||
47 | #define TMIOFB_ACC_TCLOR(x) (0x0A000000 | ((x) & 0x0000ffff)) | ||
48 | #define TMIOFB_ACC_FILL(x) (0x0B000000 | ((x) & 0x0000ffff)) | ||
49 | #define TMIOFB_ACC_DSADR(x) (0x0C000000 | ((x) & 0x00fffffe)) | ||
50 | #define TMIOFB_ACC_SSADR(x) (0x0D000000 | ((x) & 0x00fffffe)) | ||
51 | #define TMIOFB_ACC_DHPIX(x) (0x0E000000 | ((x) & 0x000003ff)) | ||
52 | #define TMIOFB_ACC_DVPIX(x) (0x0F000000 | ((x) & 0x000003ff)) | ||
53 | #define TMIOFB_ACC_SHPIX(x) (0x10000000 | ((x) & 0x000003ff)) | ||
54 | #define TMIOFB_ACC_SVPIX(x) (0x11000000 | ((x) & 0x000003ff)) | ||
55 | #define TMIOFB_ACC_LBINI(x) (0x12000000 | ((x) & 0x0000ffff)) | ||
56 | #define TMIOFB_ACC_LBK2(x) (0x13000000 | ((x) & 0x0000ffff)) | ||
57 | #define TMIOFB_ACC_SHBINI(x) (0x14000000 | ((x) & 0x0000ffff)) | ||
58 | #define TMIOFB_ACC_SHBK2(x) (0x15000000 | ((x) & 0x0000ffff)) | ||
59 | #define TMIOFB_ACC_SVBINI(x) (0x16000000 | ((x) & 0x0000ffff)) | ||
60 | #define TMIOFB_ACC_SVBK2(x) (0x17000000 | ((x) & 0x0000ffff)) | ||
61 | |||
62 | #define TMIOFB_ACC_CMGO 0x20000000 | ||
63 | #define TMIOFB_ACC_CMGO_CEND 0x00000001 | ||
64 | #define TMIOFB_ACC_CMGO_INT 0x00000002 | ||
65 | #define TMIOFB_ACC_CMGO_CMOD 0x00000010 | ||
66 | #define TMIOFB_ACC_CMGO_CDVRV 0x00000020 | ||
67 | #define TMIOFB_ACC_CMGO_CDHRV 0x00000040 | ||
68 | #define TMIOFB_ACC_CMGO_RUND 0x00008000 | ||
69 | #define TMIOFB_ACC_SCGO 0x21000000 | ||
70 | #define TMIOFB_ACC_SCGO_CEND 0x00000001 | ||
71 | #define TMIOFB_ACC_SCGO_INT 0x00000002 | ||
72 | #define TMIOFB_ACC_SCGO_ROP3 0x00000004 | ||
73 | #define TMIOFB_ACC_SCGO_TRNS 0x00000008 | ||
74 | #define TMIOFB_ACC_SCGO_DVRV 0x00000010 | ||
75 | #define TMIOFB_ACC_SCGO_DHRV 0x00000020 | ||
76 | #define TMIOFB_ACC_SCGO_SVRV 0x00000040 | ||
77 | #define TMIOFB_ACC_SCGO_SHRV 0x00000080 | ||
78 | #define TMIOFB_ACC_SCGO_DSTXY 0x00008000 | ||
79 | #define TMIOFB_ACC_SBGO 0x22000000 | ||
80 | #define TMIOFB_ACC_SBGO_CEND 0x00000001 | ||
81 | #define TMIOFB_ACC_SBGO_INT 0x00000002 | ||
82 | #define TMIOFB_ACC_SBGO_DVRV 0x00000010 | ||
83 | #define TMIOFB_ACC_SBGO_DHRV 0x00000020 | ||
84 | #define TMIOFB_ACC_SBGO_SVRV 0x00000040 | ||
85 | #define TMIOFB_ACC_SBGO_SHRV 0x00000080 | ||
86 | #define TMIOFB_ACC_SBGO_SBMD 0x00000100 | ||
87 | #define TMIOFB_ACC_FLGO 0x23000000 | ||
88 | #define TMIOFB_ACC_FLGO_CEND 0x00000001 | ||
89 | #define TMIOFB_ACC_FLGO_INT 0x00000002 | ||
90 | #define TMIOFB_ACC_FLGO_ROP3 0x00000004 | ||
91 | #define TMIOFB_ACC_LDGO 0x24000000 | ||
92 | #define TMIOFB_ACC_LDGO_CEND 0x00000001 | ||
93 | #define TMIOFB_ACC_LDGO_INT 0x00000002 | ||
94 | #define TMIOFB_ACC_LDGO_ROP3 0x00000004 | ||
95 | #define TMIOFB_ACC_LDGO_ENDPX 0x00000008 | ||
96 | #define TMIOFB_ACC_LDGO_LVRV 0x00000010 | ||
97 | #define TMIOFB_ACC_LDGO_LHRV 0x00000020 | ||
98 | #define TMIOFB_ACC_LDGO_LDMOD 0x00000040 | ||
99 | |||
100 | /* a FIFO is always allocated, even if acceleration is not used */ | ||
101 | #define TMIOFB_FIFO_SIZE 512 | ||
102 | |||
103 | /* | ||
104 | * LCD Host Controller Configuration Register | ||
105 | * | ||
106 | * This iomem area supports only 16-bit IO. | ||
107 | */ | ||
108 | #define CCR_CMD 0x04 /* Command */ | ||
109 | #define CCR_REVID 0x08 /* Revision ID */ | ||
110 | #define CCR_BASEL 0x10 /* LCD Control Reg Base Addr Low */ | ||
111 | #define CCR_BASEH 0x12 /* LCD Control Reg Base Addr High */ | ||
112 | #define CCR_UGCC 0x40 /* Unified Gated Clock Control */ | ||
113 | #define CCR_GCC 0x42 /* Gated Clock Control */ | ||
114 | #define CCR_USC 0x50 /* Unified Software Clear */ | ||
115 | #define CCR_VRAMRTC 0x60 /* VRAM Timing Control */ | ||
116 | /* 0x61 VRAM Refresh Control */ | ||
117 | #define CCR_VRAMSAC 0x62 /* VRAM Access Control */ | ||
118 | /* 0x63 VRAM Status */ | ||
119 | #define CCR_VRAMBC 0x64 /* VRAM Block Control */ | ||
120 | |||
121 | /* | ||
122 | * LCD Control Register | ||
123 | * | ||
124 | * This iomem area supports only 16-bit IO. | ||
125 | */ | ||
126 | #define LCR_UIS 0x000 /* Unified Interrupt Status */ | ||
127 | #define LCR_VHPN 0x008 /* VRAM Horizontal Pixel Number */ | ||
128 | #define LCR_CFSAL 0x00a /* Command FIFO Start Address Low */ | ||
129 | #define LCR_CFSAH 0x00c /* Command FIFO Start Address High */ | ||
130 | #define LCR_CFS 0x00e /* Command FIFO Size */ | ||
131 | #define LCR_CFWS 0x010 /* Command FIFO Writeable Size */ | ||
132 | #define LCR_BBIE 0x012 /* BitBLT Interrupt Enable */ | ||
133 | #define LCR_BBISC 0x014 /* BitBLT Interrupt Status and Clear */ | ||
134 | #define LCR_CCS 0x016 /* Command Count Status */ | ||
135 | #define LCR_BBES 0x018 /* BitBLT Execution Status */ | ||
136 | #define LCR_CMDL 0x01c /* Command Low */ | ||
137 | #define LCR_CMDH 0x01e /* Command High */ | ||
138 | #define LCR_CFC 0x022 /* Command FIFO Clear */ | ||
139 | #define LCR_CCIFC 0x024 /* CMOS Camera IF Control */ | ||
140 | #define LCR_HWT 0x026 /* Hardware Test */ | ||
141 | #define LCR_LCDCCRC 0x100 /* LCDC Clock and Reset Control */ | ||
142 | #define LCR_LCDCC 0x102 /* LCDC Control */ | ||
143 | #define LCR_LCDCOPC 0x104 /* LCDC Output Pin Control */ | ||
144 | #define LCR_LCDIS 0x108 /* LCD Interrupt Status */ | ||
145 | #define LCR_LCDIM 0x10a /* LCD Interrupt Mask */ | ||
146 | #define LCR_LCDIE 0x10c /* LCD Interrupt Enable */ | ||
147 | #define LCR_GDSAL 0x122 /* Graphics Display Start Address Low */ | ||
148 | #define LCR_GDSAH 0x124 /* Graphics Display Start Address High */ | ||
149 | #define LCR_VHPCL 0x12a /* VRAM Horizontal Pixel Count Low */ | ||
150 | #define LCR_VHPCH 0x12c /* VRAM Horizontal Pixel Count High */ | ||
151 | #define LCR_GM 0x12e /* Graphic Mode(VRAM access enable) */ | ||
152 | #define LCR_HT 0x140 /* Horizontal Total */ | ||
153 | #define LCR_HDS 0x142 /* Horizontal Display Start */ | ||
154 | #define LCR_HSS 0x144 /* H-Sync Start */ | ||
155 | #define LCR_HSE 0x146 /* H-Sync End */ | ||
156 | #define LCR_HNP 0x14c /* Horizontal Number of Pixels */ | ||
157 | #define LCR_VT 0x150 /* Vertical Total */ | ||
158 | #define LCR_VDS 0x152 /* Vertical Display Start */ | ||
159 | #define LCR_VSS 0x154 /* V-Sync Start */ | ||
160 | #define LCR_VSE 0x156 /* V-Sync End */ | ||
161 | #define LCR_CDLN 0x160 /* Current Display Line Number */ | ||
162 | #define LCR_ILN 0x162 /* Interrupt Line Number */ | ||
163 | #define LCR_SP 0x164 /* Sync Polarity */ | ||
164 | #define LCR_MISC 0x166 /* MISC(RGB565 mode) */ | ||
165 | #define LCR_VIHSS 0x16a /* Video Interface H-Sync Start */ | ||
166 | #define LCR_VIVS 0x16c /* Video Interface Vertical Start */ | ||
167 | #define LCR_VIVE 0x16e /* Video Interface Vertical End */ | ||
168 | #define LCR_VIVSS 0x170 /* Video Interface V-Sync Start */ | ||
169 | #define LCR_VCCIS 0x17e /* Video / CMOS Camera Interface Select */ | ||
170 | #define LCR_VIDWSAL 0x180 /* VI Data Write Start Address Low */ | ||
171 | #define LCR_VIDWSAH 0x182 /* VI Data Write Start Address High */ | ||
172 | #define LCR_VIDRSAL 0x184 /* VI Data Read Start Address Low */ | ||
173 | #define LCR_VIDRSAH 0x186 /* VI Data Read Start Address High */ | ||
174 | #define LCR_VIPDDST 0x188 /* VI Picture Data Display Start Timing */ | ||
175 | #define LCR_VIPDDET 0x186 /* VI Picture Data Display End Timing */ | ||
176 | #define LCR_VIE 0x18c /* Video Interface Enable */ | ||
177 | #define LCR_VCS 0x18e /* Video/Camera Select */ | ||
178 | #define LCR_VPHWC 0x194 /* Video Picture Horizontal Wait Count */ | ||
179 | #define LCR_VPHS 0x196 /* Video Picture Horizontal Size */ | ||
180 | #define LCR_VPVWC 0x198 /* Video Picture Vertical Wait Count */ | ||
181 | #define LCR_VPVS 0x19a /* Video Picture Vertical Size */ | ||
182 | #define LCR_PLHPIX 0x1a0 /* PLHPIX */ | ||
183 | #define LCR_XS 0x1a2 /* XStart */ | ||
184 | #define LCR_XCKHW 0x1a4 /* XCK High Width */ | ||
185 | #define LCR_STHS 0x1a8 /* STH Start */ | ||
186 | #define LCR_VT2 0x1aa /* Vertical Total */ | ||
187 | #define LCR_YCKSW 0x1ac /* YCK Start Wait */ | ||
188 | #define LCR_YSTS 0x1ae /* YST Start */ | ||
189 | #define LCR_PPOLS 0x1b0 /* #PPOL Start */ | ||
190 | #define LCR_PRECW 0x1b2 /* PREC Width */ | ||
191 | #define LCR_VCLKHW 0x1b4 /* VCLK High Width */ | ||
192 | #define LCR_OC 0x1b6 /* Output Control */ | ||
193 | |||
194 | static char *mode_option __devinitdata; | ||
195 | |||
196 | struct tmiofb_par { | ||
197 | u32 pseudo_palette[16]; | ||
198 | |||
199 | #ifdef CONFIG_FB_TMIO_ACCELL | ||
200 | wait_queue_head_t wait_acc; | ||
201 | bool use_polling; | ||
202 | #endif | ||
203 | |||
204 | void __iomem *ccr; | ||
205 | void __iomem *lcr; | ||
206 | }; | ||
207 | |||
208 | /*--------------------------------------------------------------------------*/ | ||
209 | |||
210 | /* | ||
211 | * reasons for an interrupt: | ||
212 | * uis bbisc lcdis | ||
213 | * 0100 0001 accelerator command completed | ||
214 | * 2000 0001 vsync start | ||
215 | * 2000 0002 display start | ||
216 | * 2000 0004 line number match(0x1ff mask???) | ||
217 | */ | ||
218 | static irqreturn_t tmiofb_irq(int irq, void *__info) | ||
219 | { | ||
220 | struct fb_info *info = __info; | ||
221 | struct tmiofb_par *par = info->par; | ||
222 | unsigned int bbisc = tmio_ioread16(par->lcr + LCR_BBISC); | ||
223 | |||
224 | |||
225 | /* | ||
226 | * We were in polling mode and now we got correct irq. | ||
227 | * Switch back to IRQ-based sync of command FIFO | ||
228 | */ | ||
229 | if (unlikely(par->use_polling && irq != -1)) { | ||
230 | printk(KERN_INFO "tmiofb: switching to waitq\n"); | ||
231 | par->use_polling = false; | ||
232 | } | ||
233 | |||
234 | tmio_iowrite16(bbisc, par->lcr + LCR_BBISC); | ||
235 | |||
236 | #ifdef CONFIG_FB_TMIO_ACCELL | ||
237 | if (bbisc & 1) | ||
238 | wake_up(&par->wait_acc); | ||
239 | #endif | ||
240 | |||
241 | return IRQ_HANDLED; | ||
242 | } | ||
243 | |||
244 | |||
245 | /*--------------------------------------------------------------------------*/ | ||
246 | |||
247 | |||
248 | /* | ||
249 | * Turns off the LCD controller and LCD host controller. | ||
250 | */ | ||
251 | static int tmiofb_hw_stop(struct platform_device *dev) | ||
252 | { | ||
253 | struct mfd_cell *cell = dev->dev.platform_data; | ||
254 | struct tmio_fb_data *data = cell->driver_data; | ||
255 | struct fb_info *info = platform_get_drvdata(dev); | ||
256 | struct tmiofb_par *par = info->par; | ||
257 | |||
258 | tmio_iowrite16(0, par->ccr + CCR_UGCC); | ||
259 | tmio_iowrite16(0, par->lcr + LCR_GM); | ||
260 | data->lcd_set_power(dev, 0); | ||
261 | tmio_iowrite16(0x0010, par->lcr + LCR_LCDCCRC); | ||
262 | |||
263 | return 0; | ||
264 | } | ||
265 | |||
266 | /* | ||
267 | * Initializes the LCD host controller. | ||
268 | */ | ||
269 | static int tmiofb_hw_init(struct platform_device *dev) | ||
270 | { | ||
271 | struct mfd_cell *cell = dev->dev.platform_data; | ||
272 | struct fb_info *info = platform_get_drvdata(dev); | ||
273 | struct tmiofb_par *par = info->par; | ||
274 | const struct resource *nlcr = &cell->resources[0]; | ||
275 | const struct resource *vram = &cell->resources[2]; | ||
276 | unsigned long base; | ||
277 | |||
278 | if (nlcr == NULL || vram == NULL) | ||
279 | return -EINVAL; | ||
280 | |||
281 | base = nlcr->start; | ||
282 | |||
283 | tmio_iowrite16(0x003a, par->ccr + CCR_UGCC); | ||
284 | tmio_iowrite16(0x003a, par->ccr + CCR_GCC); | ||
285 | tmio_iowrite16(0x3f00, par->ccr + CCR_USC); | ||
286 | |||
287 | msleep(2); /* wait for device to settle */ | ||
288 | |||
289 | tmio_iowrite16(0x0000, par->ccr + CCR_USC); | ||
290 | tmio_iowrite16(base >> 16, par->ccr + CCR_BASEH); | ||
291 | tmio_iowrite16(base, par->ccr + CCR_BASEL); | ||
292 | tmio_iowrite16(0x0002, par->ccr + CCR_CMD); /* base address enable */ | ||
293 | tmio_iowrite16(0x40a8, par->ccr + CCR_VRAMRTC); /* VRAMRC, VRAMTC */ | ||
294 | tmio_iowrite16(0x0018, par->ccr + CCR_VRAMSAC); /* VRAMSTS, VRAMAC */ | ||
295 | tmio_iowrite16(0x0002, par->ccr + CCR_VRAMBC); | ||
296 | msleep(2); /* wait for device to settle */ | ||
297 | tmio_iowrite16(0x000b, par->ccr + CCR_VRAMBC); | ||
298 | |||
299 | base = vram->start + info->screen_size; | ||
300 | tmio_iowrite16(base >> 16, par->lcr + LCR_CFSAH); | ||
301 | tmio_iowrite16(base, par->lcr + LCR_CFSAL); | ||
302 | tmio_iowrite16(TMIOFB_FIFO_SIZE - 1, par->lcr + LCR_CFS); | ||
303 | tmio_iowrite16(1, par->lcr + LCR_CFC); | ||
304 | tmio_iowrite16(1, par->lcr + LCR_BBIE); | ||
305 | tmio_iowrite16(0, par->lcr + LCR_CFWS); | ||
306 | |||
307 | return 0; | ||
308 | } | ||
309 | |||
310 | /* | ||
311 | * Sets the LCD controller's output resolution and pixel clock | ||
312 | */ | ||
313 | static void tmiofb_hw_mode(struct platform_device *dev) | ||
314 | { | ||
315 | struct mfd_cell *cell = dev->dev.platform_data; | ||
316 | struct tmio_fb_data *data = cell->driver_data; | ||
317 | struct fb_info *info = platform_get_drvdata(dev); | ||
318 | struct fb_videomode *mode = info->mode; | ||
319 | struct tmiofb_par *par = info->par; | ||
320 | unsigned int i; | ||
321 | |||
322 | tmio_iowrite16(0, par->lcr + LCR_GM); | ||
323 | data->lcd_set_power(dev, 0); | ||
324 | tmio_iowrite16(0x0010, par->lcr + LCR_LCDCCRC); | ||
325 | data->lcd_mode(dev, mode); | ||
326 | data->lcd_set_power(dev, 1); | ||
327 | |||
328 | tmio_iowrite16(info->fix.line_length, par->lcr + LCR_VHPN); | ||
329 | tmio_iowrite16(0, par->lcr + LCR_GDSAH); | ||
330 | tmio_iowrite16(0, par->lcr + LCR_GDSAL); | ||
331 | tmio_iowrite16(info->fix.line_length >> 16, par->lcr + LCR_VHPCH); | ||
332 | tmio_iowrite16(info->fix.line_length, par->lcr + LCR_VHPCL); | ||
333 | tmio_iowrite16(i = 0, par->lcr + LCR_HSS); | ||
334 | tmio_iowrite16(i += mode->hsync_len, par->lcr + LCR_HSE); | ||
335 | tmio_iowrite16(i += mode->left_margin, par->lcr + LCR_HDS); | ||
336 | tmio_iowrite16(i += mode->xres + mode->right_margin, par->lcr + LCR_HT); | ||
337 | tmio_iowrite16(mode->xres, par->lcr + LCR_HNP); | ||
338 | tmio_iowrite16(i = 0, par->lcr + LCR_VSS); | ||
339 | tmio_iowrite16(i += mode->vsync_len, par->lcr + LCR_VSE); | ||
340 | tmio_iowrite16(i += mode->upper_margin, par->lcr + LCR_VDS); | ||
341 | tmio_iowrite16(i += mode->yres, par->lcr + LCR_ILN); | ||
342 | tmio_iowrite16(i += mode->lower_margin, par->lcr + LCR_VT); | ||
343 | tmio_iowrite16(3, par->lcr + LCR_MISC); /* RGB565 mode */ | ||
344 | tmio_iowrite16(1, par->lcr + LCR_GM); /* VRAM enable */ | ||
345 | tmio_iowrite16(0x4007, par->lcr + LCR_LCDCC); | ||
346 | tmio_iowrite16(3, par->lcr + LCR_SP); /* sync polarity */ | ||
347 | |||
348 | tmio_iowrite16(0x0010, par->lcr + LCR_LCDCCRC); | ||
349 | msleep(5); /* wait for device to settle */ | ||
350 | tmio_iowrite16(0x0014, par->lcr + LCR_LCDCCRC); /* STOP_CKP */ | ||
351 | msleep(5); /* wait for device to settle */ | ||
352 | tmio_iowrite16(0x0015, par->lcr + LCR_LCDCCRC); /* STOP_CKP|SOFT_RESET*/ | ||
353 | tmio_iowrite16(0xfffa, par->lcr + LCR_VCS); | ||
354 | } | ||
355 | |||
356 | /*--------------------------------------------------------------------------*/ | ||
357 | |||
358 | #ifdef CONFIG_FB_TMIO_ACCELL | ||
359 | static int __must_check | ||
360 | tmiofb_acc_wait(struct fb_info *info, unsigned int ccs) | ||
361 | { | ||
362 | struct tmiofb_par *par = info->par; | ||
363 | /* | ||
364 | * This code can be called whith interrupts disabled. | ||
365 | * So instead of relaying on irq to trigger the event, | ||
366 | * poll the state till the necessary command is executed. | ||
367 | */ | ||
368 | if (irqs_disabled() || par->use_polling) { | ||
369 | int i = 0; | ||
370 | while (tmio_ioread16(par->lcr + LCR_CCS) > ccs) { | ||
371 | udelay(1); | ||
372 | i++; | ||
373 | if (i > 10000) { | ||
374 | pr_err("tmiofb: timeout waiting for %d\n", | ||
375 | ccs); | ||
376 | return -ETIMEDOUT; | ||
377 | } | ||
378 | tmiofb_irq(-1, info); | ||
379 | } | ||
380 | } else { | ||
381 | if (!wait_event_interruptible_timeout(par->wait_acc, | ||
382 | tmio_ioread16(par->lcr + LCR_CCS) <= ccs, | ||
383 | 1000)) { | ||
384 | pr_err("tmiofb: timeout waiting for %d\n", ccs); | ||
385 | return -ETIMEDOUT; | ||
386 | } | ||
387 | } | ||
388 | |||
389 | return 0; | ||
390 | } | ||
391 | |||
392 | /* | ||
393 | * Writes an accelerator command to the accelerator's FIFO. | ||
394 | */ | ||
395 | static int | ||
396 | tmiofb_acc_write(struct fb_info *info, const u32 *cmd, unsigned int count) | ||
397 | { | ||
398 | struct tmiofb_par *par = info->par; | ||
399 | int ret; | ||
400 | |||
401 | ret = tmiofb_acc_wait(info, TMIOFB_FIFO_SIZE - count); | ||
402 | if (ret) | ||
403 | return ret; | ||
404 | |||
405 | for (; count; count--, cmd++) { | ||
406 | tmio_iowrite16(*cmd >> 16, par->lcr + LCR_CMDH); | ||
407 | tmio_iowrite16(*cmd, par->lcr + LCR_CMDL); | ||
408 | } | ||
409 | |||
410 | return ret; | ||
411 | } | ||
412 | |||
413 | /* | ||
414 | * Wait for the accelerator to finish its operations before writing | ||
415 | * to the framebuffer for consistent display output. | ||
416 | */ | ||
417 | static int tmiofb_sync(struct fb_info *fbi) | ||
418 | { | ||
419 | struct tmiofb_par *par = fbi->par; | ||
420 | |||
421 | int ret; | ||
422 | int i = 0; | ||
423 | |||
424 | ret = tmiofb_acc_wait(fbi, 0); | ||
425 | |||
426 | while (tmio_ioread16(par->lcr + LCR_BBES) & 2) { /* blit active */ | ||
427 | udelay(1); | ||
428 | i++ ; | ||
429 | if (i > 10000) { | ||
430 | printk(KERN_ERR "timeout waiting for blit to end!\n"); | ||
431 | return -ETIMEDOUT; | ||
432 | } | ||
433 | } | ||
434 | |||
435 | return ret; | ||
436 | } | ||
437 | |||
438 | static void | ||
439 | tmiofb_fillrect(struct fb_info *fbi, const struct fb_fillrect *rect) | ||
440 | { | ||
441 | const u32 cmd[] = { | ||
442 | TMIOFB_ACC_DSADR((rect->dy * fbi->mode->xres + rect->dx) * 2), | ||
443 | TMIOFB_ACC_DHPIX(rect->width - 1), | ||
444 | TMIOFB_ACC_DVPIX(rect->height - 1), | ||
445 | TMIOFB_ACC_FILL(rect->color), | ||
446 | TMIOFB_ACC_FLGO, | ||
447 | }; | ||
448 | |||
449 | if (fbi->state != FBINFO_STATE_RUNNING || | ||
450 | fbi->flags & FBINFO_HWACCEL_DISABLED) { | ||
451 | cfb_fillrect(fbi, rect); | ||
452 | return; | ||
453 | } | ||
454 | |||
455 | tmiofb_acc_write(fbi, cmd, ARRAY_SIZE(cmd)); | ||
456 | } | ||
457 | |||
458 | static void | ||
459 | tmiofb_copyarea(struct fb_info *fbi, const struct fb_copyarea *area) | ||
460 | { | ||
461 | const u32 cmd[] = { | ||
462 | TMIOFB_ACC_DSADR((area->dy * fbi->mode->xres + area->dx) * 2), | ||
463 | TMIOFB_ACC_DHPIX(area->width - 1), | ||
464 | TMIOFB_ACC_DVPIX(area->height - 1), | ||
465 | TMIOFB_ACC_SSADR((area->sy * fbi->mode->xres + area->sx) * 2), | ||
466 | TMIOFB_ACC_SCGO, | ||
467 | }; | ||
468 | |||
469 | if (fbi->state != FBINFO_STATE_RUNNING || | ||
470 | fbi->flags & FBINFO_HWACCEL_DISABLED) { | ||
471 | cfb_copyarea(fbi, area); | ||
472 | return; | ||
473 | } | ||
474 | |||
475 | tmiofb_acc_write(fbi, cmd, ARRAY_SIZE(cmd)); | ||
476 | } | ||
477 | #endif | ||
478 | |||
479 | static void tmiofb_clearscreen(struct fb_info *info) | ||
480 | { | ||
481 | const struct fb_fillrect rect = { | ||
482 | .dx = 0, | ||
483 | .dy = 0, | ||
484 | .width = info->mode->xres, | ||
485 | .height = info->mode->yres, | ||
486 | .color = 0, | ||
487 | .rop = ROP_COPY, | ||
488 | }; | ||
489 | |||
490 | info->fbops->fb_fillrect(info, &rect); | ||
491 | } | ||
492 | |||
493 | static int tmiofb_vblank(struct fb_info *fbi, struct fb_vblank *vblank) | ||
494 | { | ||
495 | struct tmiofb_par *par = fbi->par; | ||
496 | struct fb_videomode *mode = fbi->mode; | ||
497 | unsigned int vcount = tmio_ioread16(par->lcr + LCR_CDLN); | ||
498 | unsigned int vds = mode->vsync_len + mode->upper_margin; | ||
499 | |||
500 | vblank->vcount = vcount; | ||
501 | vblank->flags = FB_VBLANK_HAVE_VBLANK | FB_VBLANK_HAVE_VCOUNT | ||
502 | | FB_VBLANK_HAVE_VSYNC; | ||
503 | |||
504 | if (vcount < mode->vsync_len) | ||
505 | vblank->flags |= FB_VBLANK_VSYNCING; | ||
506 | |||
507 | if (vcount < vds || vcount > vds + mode->yres) | ||
508 | vblank->flags |= FB_VBLANK_VBLANKING; | ||
509 | |||
510 | return 0; | ||
511 | } | ||
512 | |||
513 | |||
514 | static int tmiofb_ioctl(struct fb_info *fbi, | ||
515 | unsigned int cmd, unsigned long arg) | ||
516 | { | ||
517 | switch (cmd) { | ||
518 | case FBIOGET_VBLANK: { | ||
519 | struct fb_vblank vblank = {0}; | ||
520 | void __user *argp = (void __user *) arg; | ||
521 | |||
522 | tmiofb_vblank(fbi, &vblank); | ||
523 | if (copy_to_user(argp, &vblank, sizeof vblank)) | ||
524 | return -EFAULT; | ||
525 | return 0; | ||
526 | } | ||
527 | |||
528 | #ifdef CONFIG_FB_TMIO_ACCELL | ||
529 | case FBIO_TMIO_ACC_SYNC: | ||
530 | tmiofb_sync(fbi); | ||
531 | return 0; | ||
532 | |||
533 | case FBIO_TMIO_ACC_WRITE: { | ||
534 | u32 __user *argp = (void __user *) arg; | ||
535 | u32 len; | ||
536 | u32 acc[16]; | ||
537 | |||
538 | if (get_user(len, argp)) | ||
539 | return -EFAULT; | ||
540 | if (len > ARRAY_SIZE(acc)) | ||
541 | return -EINVAL; | ||
542 | if (copy_from_user(acc, argp + 1, sizeof(u32) * len)) | ||
543 | return -EFAULT; | ||
544 | |||
545 | return tmiofb_acc_write(fbi, acc, len); | ||
546 | } | ||
547 | #endif | ||
548 | } | ||
549 | |||
550 | return -ENOTTY; | ||
551 | } | ||
552 | |||
553 | /*--------------------------------------------------------------------------*/ | ||
554 | |||
555 | /* Select the smallest mode that allows the desired resolution to be | ||
556 | * displayed. If desired, the x and y parameters can be rounded up to | ||
557 | * match the selected mode. | ||
558 | */ | ||
559 | static struct fb_videomode * | ||
560 | tmiofb_find_mode(struct fb_info *info, struct fb_var_screeninfo *var) | ||
561 | { | ||
562 | struct mfd_cell *cell = | ||
563 | info->device->platform_data; | ||
564 | struct tmio_fb_data *data = cell->driver_data; | ||
565 | struct fb_videomode *best = NULL; | ||
566 | int i; | ||
567 | |||
568 | for (i = 0; i < data->num_modes; i++) { | ||
569 | struct fb_videomode *mode = data->modes + i; | ||
570 | |||
571 | if (mode->xres >= var->xres && mode->yres >= var->yres | ||
572 | && (!best || (mode->xres < best->xres | ||
573 | && mode->yres < best->yres))) | ||
574 | best = mode; | ||
575 | } | ||
576 | |||
577 | return best; | ||
578 | } | ||
579 | |||
580 | static int tmiofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) | ||
581 | { | ||
582 | |||
583 | struct fb_videomode *mode; | ||
584 | struct mfd_cell *cell = | ||
585 | info->device->platform_data; | ||
586 | struct tmio_fb_data *data = cell->driver_data; | ||
587 | |||
588 | mode = tmiofb_find_mode(info, var); | ||
589 | if (!mode || var->bits_per_pixel > 16) | ||
590 | return -EINVAL; | ||
591 | |||
592 | fb_videomode_to_var(var, mode); | ||
593 | |||
594 | var->xres_virtual = mode->xres; | ||
595 | var->yres_virtual = info->screen_size / (mode->xres * 2); | ||
596 | |||
597 | if (var->yres_virtual < var->yres) | ||
598 | return -EINVAL; | ||
599 | |||
600 | var->xoffset = 0; | ||
601 | var->yoffset = 0; | ||
602 | var->bits_per_pixel = 16; | ||
603 | var->grayscale = 0; | ||
604 | var->red.offset = 11; | ||
605 | var->red.length = 5; | ||
606 | var->green.offset = 5; | ||
607 | var->green.length = 6; | ||
608 | var->blue.offset = 0; | ||
609 | var->blue.length = 5; | ||
610 | var->transp.offset = 0; | ||
611 | var->transp.length = 0; | ||
612 | var->nonstd = 0; | ||
613 | var->height = data->height; /* mm */ | ||
614 | var->width = data->width; /* mm */ | ||
615 | var->rotate = 0; | ||
616 | return 0; | ||
617 | } | ||
618 | |||
619 | static int tmiofb_set_par(struct fb_info *info) | ||
620 | { | ||
621 | struct fb_var_screeninfo *var = &info->var; | ||
622 | struct fb_videomode *mode; | ||
623 | |||
624 | mode = tmiofb_find_mode(info, var); | ||
625 | if (!mode) | ||
626 | return -EINVAL; | ||
627 | |||
628 | info->mode = mode; | ||
629 | info->fix.line_length = info->mode->xres * | ||
630 | var->bits_per_pixel / 8; | ||
631 | |||
632 | tmiofb_hw_mode(to_platform_device(info->device)); | ||
633 | tmiofb_clearscreen(info); | ||
634 | return 0; | ||
635 | } | ||
636 | |||
637 | static int tmiofb_setcolreg(unsigned regno, unsigned red, unsigned green, | ||
638 | unsigned blue, unsigned transp, | ||
639 | struct fb_info *info) | ||
640 | { | ||
641 | struct tmiofb_par *par = info->par; | ||
642 | |||
643 | if (regno < ARRAY_SIZE(par->pseudo_palette)) { | ||
644 | par->pseudo_palette[regno] = | ||
645 | ((red & 0xf800)) | | ||
646 | ((green & 0xfc00) >> 5) | | ||
647 | ((blue & 0xf800) >> 11); | ||
648 | return 0; | ||
649 | } | ||
650 | |||
651 | return -EINVAL; | ||
652 | } | ||
653 | |||
654 | static int tmiofb_blank(int blank, struct fb_info *info) | ||
655 | { | ||
656 | /* | ||
657 | * everything is done in lcd/bl drivers. | ||
658 | * this is purely to make sysfs happy and work. | ||
659 | */ | ||
660 | return 0; | ||
661 | } | ||
662 | |||
663 | static struct fb_ops tmiofb_ops = { | ||
664 | .owner = THIS_MODULE, | ||
665 | |||
666 | .fb_ioctl = tmiofb_ioctl, | ||
667 | .fb_check_var = tmiofb_check_var, | ||
668 | .fb_set_par = tmiofb_set_par, | ||
669 | .fb_setcolreg = tmiofb_setcolreg, | ||
670 | .fb_blank = tmiofb_blank, | ||
671 | .fb_imageblit = cfb_imageblit, | ||
672 | #ifdef CONFIG_FB_TMIO_ACCELL | ||
673 | .fb_sync = tmiofb_sync, | ||
674 | .fb_fillrect = tmiofb_fillrect, | ||
675 | .fb_copyarea = tmiofb_copyarea, | ||
676 | #else | ||
677 | .fb_fillrect = cfb_fillrect, | ||
678 | .fb_copyarea = cfb_copyarea, | ||
679 | #endif | ||
680 | }; | ||
681 | |||
682 | /*--------------------------------------------------------------------------*/ | ||
683 | |||
684 | static int __devinit tmiofb_probe(struct platform_device *dev) | ||
685 | { | ||
686 | struct mfd_cell *cell = dev->dev.platform_data; | ||
687 | struct tmio_fb_data *data = cell->driver_data; | ||
688 | struct resource *ccr = platform_get_resource(dev, IORESOURCE_MEM, 1); | ||
689 | struct resource *lcr = platform_get_resource(dev, IORESOURCE_MEM, 0); | ||
690 | struct resource *vram = platform_get_resource(dev, IORESOURCE_MEM, 2); | ||
691 | int irq = platform_get_irq(dev, 0); | ||
692 | struct fb_info *info; | ||
693 | struct tmiofb_par *par; | ||
694 | int retval; | ||
695 | |||
696 | /* | ||
697 | * This is the only way ATM to disable the fb | ||
698 | */ | ||
699 | if (data == NULL) { | ||
700 | dev_err(&dev->dev, "NULL platform data!\n"); | ||
701 | return -EINVAL; | ||
702 | } | ||
703 | |||
704 | info = framebuffer_alloc(sizeof(struct tmiofb_par), &dev->dev); | ||
705 | |||
706 | if (!info) | ||
707 | return -ENOMEM; | ||
708 | |||
709 | par = info->par; | ||
710 | |||
711 | #ifdef CONFIG_FB_TMIO_ACCELL | ||
712 | init_waitqueue_head(&par->wait_acc); | ||
713 | |||
714 | par->use_polling = true; | ||
715 | |||
716 | info->flags = FBINFO_DEFAULT | FBINFO_HWACCEL_COPYAREA | ||
717 | | FBINFO_HWACCEL_FILLRECT; | ||
718 | #else | ||
719 | info->flags = FBINFO_DEFAULT; | ||
720 | #endif | ||
721 | |||
722 | info->fbops = &tmiofb_ops; | ||
723 | |||
724 | strcpy(info->fix.id, "tmio-fb"); | ||
725 | info->fix.smem_start = vram->start; | ||
726 | info->fix.smem_len = resource_size(vram); | ||
727 | info->fix.type = FB_TYPE_PACKED_PIXELS; | ||
728 | info->fix.visual = FB_VISUAL_TRUECOLOR; | ||
729 | info->fix.mmio_start = lcr->start; | ||
730 | info->fix.mmio_len = resource_size(lcr); | ||
731 | info->fix.accel = FB_ACCEL_NONE; | ||
732 | info->screen_size = info->fix.smem_len - (4 * TMIOFB_FIFO_SIZE); | ||
733 | info->pseudo_palette = par->pseudo_palette; | ||
734 | |||
735 | par->ccr = ioremap(ccr->start, resource_size(ccr)); | ||
736 | if (!par->ccr) { | ||
737 | retval = -ENOMEM; | ||
738 | goto err_ioremap_ccr; | ||
739 | } | ||
740 | |||
741 | par->lcr = ioremap(info->fix.mmio_start, info->fix.mmio_len); | ||
742 | if (!par->lcr) { | ||
743 | retval = -ENOMEM; | ||
744 | goto err_ioremap_lcr; | ||
745 | } | ||
746 | |||
747 | info->screen_base = ioremap(info->fix.smem_start, info->fix.smem_len); | ||
748 | if (!info->screen_base) { | ||
749 | retval = -ENOMEM; | ||
750 | goto err_ioremap_vram; | ||
751 | } | ||
752 | |||
753 | retval = request_irq(irq, &tmiofb_irq, IRQF_DISABLED, | ||
754 | dev->dev.bus_id, info); | ||
755 | |||
756 | if (retval) | ||
757 | goto err_request_irq; | ||
758 | |||
759 | platform_set_drvdata(dev, info); | ||
760 | |||
761 | retval = fb_find_mode(&info->var, info, mode_option, | ||
762 | data->modes, data->num_modes, | ||
763 | data->modes, 16); | ||
764 | if (!retval) { | ||
765 | retval = -EINVAL; | ||
766 | goto err_find_mode; | ||
767 | } | ||
768 | |||
769 | if (cell->enable) { | ||
770 | retval = cell->enable(dev); | ||
771 | if (retval) | ||
772 | goto err_enable; | ||
773 | } | ||
774 | |||
775 | retval = tmiofb_hw_init(dev); | ||
776 | if (retval) | ||
777 | goto err_hw_init; | ||
778 | |||
779 | fb_videomode_to_modelist(data->modes, data->num_modes, | ||
780 | &info->modelist); | ||
781 | |||
782 | retval = register_framebuffer(info); | ||
783 | if (retval < 0) | ||
784 | goto err_register_framebuffer; | ||
785 | |||
786 | printk(KERN_INFO "fb%d: %s frame buffer device\n", | ||
787 | info->node, info->fix.id); | ||
788 | |||
789 | return 0; | ||
790 | |||
791 | err_register_framebuffer: | ||
792 | /*err_set_par:*/ | ||
793 | tmiofb_hw_stop(dev); | ||
794 | err_hw_init: | ||
795 | if (cell->disable) | ||
796 | cell->disable(dev); | ||
797 | err_enable: | ||
798 | err_find_mode: | ||
799 | platform_set_drvdata(dev, NULL); | ||
800 | free_irq(irq, info); | ||
801 | err_request_irq: | ||
802 | iounmap(info->screen_base); | ||
803 | err_ioremap_vram: | ||
804 | iounmap(par->lcr); | ||
805 | err_ioremap_lcr: | ||
806 | iounmap(par->ccr); | ||
807 | err_ioremap_ccr: | ||
808 | framebuffer_release(info); | ||
809 | return retval; | ||
810 | } | ||
811 | |||
812 | static int __devexit tmiofb_remove(struct platform_device *dev) | ||
813 | { | ||
814 | struct mfd_cell *cell = dev->dev.platform_data; | ||
815 | struct fb_info *info = platform_get_drvdata(dev); | ||
816 | int irq = platform_get_irq(dev, 0); | ||
817 | struct tmiofb_par *par; | ||
818 | |||
819 | if (info) { | ||
820 | par = info->par; | ||
821 | unregister_framebuffer(info); | ||
822 | |||
823 | tmiofb_hw_stop(dev); | ||
824 | |||
825 | if (cell->disable) | ||
826 | cell->disable(dev); | ||
827 | |||
828 | platform_set_drvdata(dev, NULL); | ||
829 | |||
830 | free_irq(irq, info); | ||
831 | |||
832 | iounmap(info->screen_base); | ||
833 | iounmap(par->lcr); | ||
834 | iounmap(par->ccr); | ||
835 | |||
836 | framebuffer_release(info); | ||
837 | } | ||
838 | |||
839 | return 0; | ||
840 | } | ||
841 | |||
842 | #ifdef DEBUG | ||
843 | static void tmiofb_dump_regs(struct platform_device *dev) | ||
844 | { | ||
845 | struct fb_info *info = platform_get_drvdata(dev); | ||
846 | struct tmiofb_par *par = info->par; | ||
847 | |||
848 | printk(KERN_DEBUG "lhccr:\n"); | ||
849 | #define CCR_PR(n) printk(KERN_DEBUG "\t" #n " = \t%04x\n",\ | ||
850 | tmio_ioread16(par->ccr + CCR_ ## n)); | ||
851 | CCR_PR(CMD); | ||
852 | CCR_PR(REVID); | ||
853 | CCR_PR(BASEL); | ||
854 | CCR_PR(BASEH); | ||
855 | CCR_PR(UGCC); | ||
856 | CCR_PR(GCC); | ||
857 | CCR_PR(USC); | ||
858 | CCR_PR(VRAMRTC); | ||
859 | CCR_PR(VRAMSAC); | ||
860 | CCR_PR(VRAMBC); | ||
861 | #undef CCR_PR | ||
862 | |||
863 | printk(KERN_DEBUG "lcr: \n"); | ||
864 | #define LCR_PR(n) printk(KERN_DEBUG "\t" #n " = \t%04x\n",\ | ||
865 | tmio_ioread16(par->lcr + LCR_ ## n)); | ||
866 | LCR_PR(UIS); | ||
867 | LCR_PR(VHPN); | ||
868 | LCR_PR(CFSAL); | ||
869 | LCR_PR(CFSAH); | ||
870 | LCR_PR(CFS); | ||
871 | LCR_PR(CFWS); | ||
872 | LCR_PR(BBIE); | ||
873 | LCR_PR(BBISC); | ||
874 | LCR_PR(CCS); | ||
875 | LCR_PR(BBES); | ||
876 | LCR_PR(CMDL); | ||
877 | LCR_PR(CMDH); | ||
878 | LCR_PR(CFC); | ||
879 | LCR_PR(CCIFC); | ||
880 | LCR_PR(HWT); | ||
881 | LCR_PR(LCDCCRC); | ||
882 | LCR_PR(LCDCC); | ||
883 | LCR_PR(LCDCOPC); | ||
884 | LCR_PR(LCDIS); | ||
885 | LCR_PR(LCDIM); | ||
886 | LCR_PR(LCDIE); | ||
887 | LCR_PR(GDSAL); | ||
888 | LCR_PR(GDSAH); | ||
889 | LCR_PR(VHPCL); | ||
890 | LCR_PR(VHPCH); | ||
891 | LCR_PR(GM); | ||
892 | LCR_PR(HT); | ||
893 | LCR_PR(HDS); | ||
894 | LCR_PR(HSS); | ||
895 | LCR_PR(HSE); | ||
896 | LCR_PR(HNP); | ||
897 | LCR_PR(VT); | ||
898 | LCR_PR(VDS); | ||
899 | LCR_PR(VSS); | ||
900 | LCR_PR(VSE); | ||
901 | LCR_PR(CDLN); | ||
902 | LCR_PR(ILN); | ||
903 | LCR_PR(SP); | ||
904 | LCR_PR(MISC); | ||
905 | LCR_PR(VIHSS); | ||
906 | LCR_PR(VIVS); | ||
907 | LCR_PR(VIVE); | ||
908 | LCR_PR(VIVSS); | ||
909 | LCR_PR(VCCIS); | ||
910 | LCR_PR(VIDWSAL); | ||
911 | LCR_PR(VIDWSAH); | ||
912 | LCR_PR(VIDRSAL); | ||
913 | LCR_PR(VIDRSAH); | ||
914 | LCR_PR(VIPDDST); | ||
915 | LCR_PR(VIPDDET); | ||
916 | LCR_PR(VIE); | ||
917 | LCR_PR(VCS); | ||
918 | LCR_PR(VPHWC); | ||
919 | LCR_PR(VPHS); | ||
920 | LCR_PR(VPVWC); | ||
921 | LCR_PR(VPVS); | ||
922 | LCR_PR(PLHPIX); | ||
923 | LCR_PR(XS); | ||
924 | LCR_PR(XCKHW); | ||
925 | LCR_PR(STHS); | ||
926 | LCR_PR(VT2); | ||
927 | LCR_PR(YCKSW); | ||
928 | LCR_PR(YSTS); | ||
929 | LCR_PR(PPOLS); | ||
930 | LCR_PR(PRECW); | ||
931 | LCR_PR(VCLKHW); | ||
932 | LCR_PR(OC); | ||
933 | #undef LCR_PR | ||
934 | } | ||
935 | #endif | ||
936 | |||
937 | #ifdef CONFIG_PM | ||
938 | static int tmiofb_suspend(struct platform_device *dev, pm_message_t state) | ||
939 | { | ||
940 | struct fb_info *info = platform_get_drvdata(dev); | ||
941 | struct tmiofb_par *par = info->par; | ||
942 | struct mfd_cell *cell = dev->dev.platform_data; | ||
943 | int retval = 0; | ||
944 | |||
945 | acquire_console_sem(); | ||
946 | |||
947 | fb_set_suspend(info, 1); | ||
948 | |||
949 | if (info->fbops->fb_sync) | ||
950 | info->fbops->fb_sync(info); | ||
951 | |||
952 | |||
953 | /* | ||
954 | * The fb should be usable even if interrupts are disabled (and they are | ||
955 | * during suspend/resume). Switch temporary to forced polling. | ||
956 | */ | ||
957 | printk(KERN_INFO "tmiofb: switching to polling\n"); | ||
958 | par->use_polling = true; | ||
959 | tmiofb_hw_stop(dev); | ||
960 | |||
961 | if (cell->suspend) | ||
962 | retval = cell->suspend(dev); | ||
963 | |||
964 | release_console_sem(); | ||
965 | |||
966 | return retval; | ||
967 | } | ||
968 | |||
969 | static int tmiofb_resume(struct platform_device *dev) | ||
970 | { | ||
971 | struct fb_info *info = platform_get_drvdata(dev); | ||
972 | struct mfd_cell *cell = dev->dev.platform_data; | ||
973 | int retval; | ||
974 | |||
975 | acquire_console_sem(); | ||
976 | |||
977 | if (cell->resume) { | ||
978 | retval = cell->resume(dev); | ||
979 | if (retval) | ||
980 | goto out; | ||
981 | } | ||
982 | |||
983 | tmiofb_irq(-1, info); | ||
984 | |||
985 | tmiofb_hw_init(dev); | ||
986 | |||
987 | tmiofb_hw_mode(dev); | ||
988 | |||
989 | fb_set_suspend(info, 0); | ||
990 | out: | ||
991 | release_console_sem(); | ||
992 | return retval; | ||
993 | } | ||
994 | #else | ||
995 | #define tmiofb_suspend NULL | ||
996 | #define tmiofb_resume NULL | ||
997 | #endif | ||
998 | |||
999 | static struct platform_driver tmiofb_driver = { | ||
1000 | .driver.name = "tmio-fb", | ||
1001 | .driver.owner = THIS_MODULE, | ||
1002 | .probe = tmiofb_probe, | ||
1003 | .remove = __devexit_p(tmiofb_remove), | ||
1004 | .suspend = tmiofb_suspend, | ||
1005 | .resume = tmiofb_resume, | ||
1006 | }; | ||
1007 | |||
1008 | /*--------------------------------------------------------------------------*/ | ||
1009 | |||
1010 | #ifndef MODULE | ||
1011 | static void __init tmiofb_setup(char *options) | ||
1012 | { | ||
1013 | char *this_opt; | ||
1014 | |||
1015 | if (!options || !*options) | ||
1016 | return; | ||
1017 | |||
1018 | while ((this_opt = strsep(&options, ",")) != NULL) { | ||
1019 | if (!*this_opt) | ||
1020 | continue; | ||
1021 | /* | ||
1022 | * FIXME | ||
1023 | */ | ||
1024 | } | ||
1025 | } | ||
1026 | #endif | ||
1027 | |||
1028 | static int __init tmiofb_init(void) | ||
1029 | { | ||
1030 | #ifndef MODULE | ||
1031 | char *option = NULL; | ||
1032 | |||
1033 | if (fb_get_options("tmiofb", &option)) | ||
1034 | return -ENODEV; | ||
1035 | tmiofb_setup(option); | ||
1036 | #endif | ||
1037 | return platform_driver_register(&tmiofb_driver); | ||
1038 | } | ||
1039 | |||
1040 | static void __exit tmiofb_cleanup(void) | ||
1041 | { | ||
1042 | platform_driver_unregister(&tmiofb_driver); | ||
1043 | } | ||
1044 | |||
1045 | module_init(tmiofb_init); | ||
1046 | module_exit(tmiofb_cleanup); | ||
1047 | |||
1048 | MODULE_DESCRIPTION("TMIO framebuffer driver"); | ||
1049 | MODULE_AUTHOR("Chris Humbert, Dirk Opfer, Dmitry Baryshkov"); | ||
1050 | MODULE_LICENSE("GPL"); | ||