Commit 6d1a05033bf0bfe236b1c5f425315967d7d684cd
Exists in
smarc-l5.0.0_1.0.0-ga
and in
5 other branches
Merge tag 'fbdev-fixes-for-3.6-1' of git://github.com/schandinat/linux-2.6
Pull fbdev fixes from Florian Tobias Schandinat: - a fix by Paul Cercueil to prevent a possible buffer overflow - a fix by Bruno Prémont to prevent a rare sleep in invalid context - a fix by Julia Lawall for a double free in auo_k190x - a fix by Dan Carpenter to prevent a division by zero in mb862xxfb - a regression fix by Tomi Valkeinen for the SDI output in OMAP - a fix by Grazvydas Ignotas to fix the console colors in OMAP * tag 'fbdev-fixes-for-3.6-1' of git://github.com/schandinat/linux-2.6: OMAPFB: fix framebuffer console colors OMAPDSS: Fix SDI PLL locking video: mb862xxfb: prevent divide by zero bug drivers/video/auo_k190x.c: drop kfree of devm_kzalloc's data fbcon: Fix bit_putcs() call to kmalloc(s, GFP_KERNEL) fbcon: prevent possible buffer overflow.
Showing 6 changed files Inline Diff
drivers/video/auo_k190x.c
1 | /* | 1 | /* |
2 | * Common code for AUO-K190X framebuffer drivers | 2 | * Common code for AUO-K190X framebuffer drivers |
3 | * | 3 | * |
4 | * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de> | 4 | * Copyright (C) 2012 Heiko Stuebner <heiko@sntech.de> |
5 | * | 5 | * |
6 | * This program is free software; you can redistribute it and/or modify | 6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License version 2 as | 7 | * it under the terms of the GNU General Public License version 2 as |
8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/module.h> | 11 | #include <linux/module.h> |
12 | #include <linux/kernel.h> | 12 | #include <linux/kernel.h> |
13 | #include <linux/gpio.h> | 13 | #include <linux/gpio.h> |
14 | #include <linux/pm_runtime.h> | 14 | #include <linux/pm_runtime.h> |
15 | #include <linux/fb.h> | 15 | #include <linux/fb.h> |
16 | #include <linux/delay.h> | 16 | #include <linux/delay.h> |
17 | #include <linux/uaccess.h> | 17 | #include <linux/uaccess.h> |
18 | #include <linux/vmalloc.h> | 18 | #include <linux/vmalloc.h> |
19 | #include <linux/regulator/consumer.h> | 19 | #include <linux/regulator/consumer.h> |
20 | 20 | ||
21 | #include <video/auo_k190xfb.h> | 21 | #include <video/auo_k190xfb.h> |
22 | 22 | ||
23 | #include "auo_k190x.h" | 23 | #include "auo_k190x.h" |
24 | 24 | ||
25 | struct panel_info { | 25 | struct panel_info { |
26 | int w; | 26 | int w; |
27 | int h; | 27 | int h; |
28 | }; | 28 | }; |
29 | 29 | ||
30 | /* table of panel specific parameters to be indexed into by the board drivers */ | 30 | /* table of panel specific parameters to be indexed into by the board drivers */ |
31 | static struct panel_info panel_table[] = { | 31 | static struct panel_info panel_table[] = { |
32 | /* standard 6" */ | 32 | /* standard 6" */ |
33 | [AUOK190X_RESOLUTION_800_600] = { | 33 | [AUOK190X_RESOLUTION_800_600] = { |
34 | .w = 800, | 34 | .w = 800, |
35 | .h = 600, | 35 | .h = 600, |
36 | }, | 36 | }, |
37 | /* standard 9" */ | 37 | /* standard 9" */ |
38 | [AUOK190X_RESOLUTION_1024_768] = { | 38 | [AUOK190X_RESOLUTION_1024_768] = { |
39 | .w = 1024, | 39 | .w = 1024, |
40 | .h = 768, | 40 | .h = 768, |
41 | }, | 41 | }, |
42 | }; | 42 | }; |
43 | 43 | ||
44 | /* | 44 | /* |
45 | * private I80 interface to the board driver | 45 | * private I80 interface to the board driver |
46 | */ | 46 | */ |
47 | 47 | ||
48 | static void auok190x_issue_data(struct auok190xfb_par *par, u16 data) | 48 | static void auok190x_issue_data(struct auok190xfb_par *par, u16 data) |
49 | { | 49 | { |
50 | par->board->set_ctl(par, AUOK190X_I80_WR, 0); | 50 | par->board->set_ctl(par, AUOK190X_I80_WR, 0); |
51 | par->board->set_hdb(par, data); | 51 | par->board->set_hdb(par, data); |
52 | par->board->set_ctl(par, AUOK190X_I80_WR, 1); | 52 | par->board->set_ctl(par, AUOK190X_I80_WR, 1); |
53 | } | 53 | } |
54 | 54 | ||
55 | static void auok190x_issue_cmd(struct auok190xfb_par *par, u16 data) | 55 | static void auok190x_issue_cmd(struct auok190xfb_par *par, u16 data) |
56 | { | 56 | { |
57 | par->board->set_ctl(par, AUOK190X_I80_DC, 0); | 57 | par->board->set_ctl(par, AUOK190X_I80_DC, 0); |
58 | auok190x_issue_data(par, data); | 58 | auok190x_issue_data(par, data); |
59 | par->board->set_ctl(par, AUOK190X_I80_DC, 1); | 59 | par->board->set_ctl(par, AUOK190X_I80_DC, 1); |
60 | } | 60 | } |
61 | 61 | ||
62 | static int auok190x_issue_pixels(struct auok190xfb_par *par, int size, | 62 | static int auok190x_issue_pixels(struct auok190xfb_par *par, int size, |
63 | u16 *data) | 63 | u16 *data) |
64 | { | 64 | { |
65 | struct device *dev = par->info->device; | 65 | struct device *dev = par->info->device; |
66 | int i; | 66 | int i; |
67 | u16 tmp; | 67 | u16 tmp; |
68 | 68 | ||
69 | if (size & 3) { | 69 | if (size & 3) { |
70 | dev_err(dev, "issue_pixels: size %d must be a multiple of 4\n", | 70 | dev_err(dev, "issue_pixels: size %d must be a multiple of 4\n", |
71 | size); | 71 | size); |
72 | return -EINVAL; | 72 | return -EINVAL; |
73 | } | 73 | } |
74 | 74 | ||
75 | for (i = 0; i < (size >> 1); i++) { | 75 | for (i = 0; i < (size >> 1); i++) { |
76 | par->board->set_ctl(par, AUOK190X_I80_WR, 0); | 76 | par->board->set_ctl(par, AUOK190X_I80_WR, 0); |
77 | 77 | ||
78 | /* simple reduction of 8bit staticgray to 4bit gray | 78 | /* simple reduction of 8bit staticgray to 4bit gray |
79 | * combines 4 * 4bit pixel values into a 16bit value | 79 | * combines 4 * 4bit pixel values into a 16bit value |
80 | */ | 80 | */ |
81 | tmp = (data[2*i] & 0xF0) >> 4; | 81 | tmp = (data[2*i] & 0xF0) >> 4; |
82 | tmp |= (data[2*i] & 0xF000) >> 8; | 82 | tmp |= (data[2*i] & 0xF000) >> 8; |
83 | tmp |= (data[2*i+1] & 0xF0) << 4; | 83 | tmp |= (data[2*i+1] & 0xF0) << 4; |
84 | tmp |= (data[2*i+1] & 0xF000); | 84 | tmp |= (data[2*i+1] & 0xF000); |
85 | 85 | ||
86 | par->board->set_hdb(par, tmp); | 86 | par->board->set_hdb(par, tmp); |
87 | par->board->set_ctl(par, AUOK190X_I80_WR, 1); | 87 | par->board->set_ctl(par, AUOK190X_I80_WR, 1); |
88 | } | 88 | } |
89 | 89 | ||
90 | return 0; | 90 | return 0; |
91 | } | 91 | } |
92 | 92 | ||
93 | static u16 auok190x_read_data(struct auok190xfb_par *par) | 93 | static u16 auok190x_read_data(struct auok190xfb_par *par) |
94 | { | 94 | { |
95 | u16 data; | 95 | u16 data; |
96 | 96 | ||
97 | par->board->set_ctl(par, AUOK190X_I80_OE, 0); | 97 | par->board->set_ctl(par, AUOK190X_I80_OE, 0); |
98 | data = par->board->get_hdb(par); | 98 | data = par->board->get_hdb(par); |
99 | par->board->set_ctl(par, AUOK190X_I80_OE, 1); | 99 | par->board->set_ctl(par, AUOK190X_I80_OE, 1); |
100 | 100 | ||
101 | return data; | 101 | return data; |
102 | } | 102 | } |
103 | 103 | ||
104 | /* | 104 | /* |
105 | * Command interface for the controller drivers | 105 | * Command interface for the controller drivers |
106 | */ | 106 | */ |
107 | 107 | ||
108 | void auok190x_send_command_nowait(struct auok190xfb_par *par, u16 data) | 108 | void auok190x_send_command_nowait(struct auok190xfb_par *par, u16 data) |
109 | { | 109 | { |
110 | par->board->set_ctl(par, AUOK190X_I80_CS, 0); | 110 | par->board->set_ctl(par, AUOK190X_I80_CS, 0); |
111 | auok190x_issue_cmd(par, data); | 111 | auok190x_issue_cmd(par, data); |
112 | par->board->set_ctl(par, AUOK190X_I80_CS, 1); | 112 | par->board->set_ctl(par, AUOK190X_I80_CS, 1); |
113 | } | 113 | } |
114 | EXPORT_SYMBOL_GPL(auok190x_send_command_nowait); | 114 | EXPORT_SYMBOL_GPL(auok190x_send_command_nowait); |
115 | 115 | ||
116 | void auok190x_send_cmdargs_nowait(struct auok190xfb_par *par, u16 cmd, | 116 | void auok190x_send_cmdargs_nowait(struct auok190xfb_par *par, u16 cmd, |
117 | int argc, u16 *argv) | 117 | int argc, u16 *argv) |
118 | { | 118 | { |
119 | int i; | 119 | int i; |
120 | 120 | ||
121 | par->board->set_ctl(par, AUOK190X_I80_CS, 0); | 121 | par->board->set_ctl(par, AUOK190X_I80_CS, 0); |
122 | auok190x_issue_cmd(par, cmd); | 122 | auok190x_issue_cmd(par, cmd); |
123 | 123 | ||
124 | for (i = 0; i < argc; i++) | 124 | for (i = 0; i < argc; i++) |
125 | auok190x_issue_data(par, argv[i]); | 125 | auok190x_issue_data(par, argv[i]); |
126 | par->board->set_ctl(par, AUOK190X_I80_CS, 1); | 126 | par->board->set_ctl(par, AUOK190X_I80_CS, 1); |
127 | } | 127 | } |
128 | EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_nowait); | 128 | EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_nowait); |
129 | 129 | ||
130 | int auok190x_send_command(struct auok190xfb_par *par, u16 data) | 130 | int auok190x_send_command(struct auok190xfb_par *par, u16 data) |
131 | { | 131 | { |
132 | int ret; | 132 | int ret; |
133 | 133 | ||
134 | ret = par->board->wait_for_rdy(par); | 134 | ret = par->board->wait_for_rdy(par); |
135 | if (ret) | 135 | if (ret) |
136 | return ret; | 136 | return ret; |
137 | 137 | ||
138 | auok190x_send_command_nowait(par, data); | 138 | auok190x_send_command_nowait(par, data); |
139 | return 0; | 139 | return 0; |
140 | } | 140 | } |
141 | EXPORT_SYMBOL_GPL(auok190x_send_command); | 141 | EXPORT_SYMBOL_GPL(auok190x_send_command); |
142 | 142 | ||
143 | int auok190x_send_cmdargs(struct auok190xfb_par *par, u16 cmd, | 143 | int auok190x_send_cmdargs(struct auok190xfb_par *par, u16 cmd, |
144 | int argc, u16 *argv) | 144 | int argc, u16 *argv) |
145 | { | 145 | { |
146 | int ret; | 146 | int ret; |
147 | 147 | ||
148 | ret = par->board->wait_for_rdy(par); | 148 | ret = par->board->wait_for_rdy(par); |
149 | if (ret) | 149 | if (ret) |
150 | return ret; | 150 | return ret; |
151 | 151 | ||
152 | auok190x_send_cmdargs_nowait(par, cmd, argc, argv); | 152 | auok190x_send_cmdargs_nowait(par, cmd, argc, argv); |
153 | return 0; | 153 | return 0; |
154 | } | 154 | } |
155 | EXPORT_SYMBOL_GPL(auok190x_send_cmdargs); | 155 | EXPORT_SYMBOL_GPL(auok190x_send_cmdargs); |
156 | 156 | ||
157 | int auok190x_read_cmdargs(struct auok190xfb_par *par, u16 cmd, | 157 | int auok190x_read_cmdargs(struct auok190xfb_par *par, u16 cmd, |
158 | int argc, u16 *argv) | 158 | int argc, u16 *argv) |
159 | { | 159 | { |
160 | int i, ret; | 160 | int i, ret; |
161 | 161 | ||
162 | ret = par->board->wait_for_rdy(par); | 162 | ret = par->board->wait_for_rdy(par); |
163 | if (ret) | 163 | if (ret) |
164 | return ret; | 164 | return ret; |
165 | 165 | ||
166 | par->board->set_ctl(par, AUOK190X_I80_CS, 0); | 166 | par->board->set_ctl(par, AUOK190X_I80_CS, 0); |
167 | auok190x_issue_cmd(par, cmd); | 167 | auok190x_issue_cmd(par, cmd); |
168 | 168 | ||
169 | for (i = 0; i < argc; i++) | 169 | for (i = 0; i < argc; i++) |
170 | argv[i] = auok190x_read_data(par); | 170 | argv[i] = auok190x_read_data(par); |
171 | par->board->set_ctl(par, AUOK190X_I80_CS, 1); | 171 | par->board->set_ctl(par, AUOK190X_I80_CS, 1); |
172 | 172 | ||
173 | return 0; | 173 | return 0; |
174 | } | 174 | } |
175 | EXPORT_SYMBOL_GPL(auok190x_read_cmdargs); | 175 | EXPORT_SYMBOL_GPL(auok190x_read_cmdargs); |
176 | 176 | ||
177 | void auok190x_send_cmdargs_pixels_nowait(struct auok190xfb_par *par, u16 cmd, | 177 | void auok190x_send_cmdargs_pixels_nowait(struct auok190xfb_par *par, u16 cmd, |
178 | int argc, u16 *argv, int size, u16 *data) | 178 | int argc, u16 *argv, int size, u16 *data) |
179 | { | 179 | { |
180 | int i; | 180 | int i; |
181 | 181 | ||
182 | par->board->set_ctl(par, AUOK190X_I80_CS, 0); | 182 | par->board->set_ctl(par, AUOK190X_I80_CS, 0); |
183 | 183 | ||
184 | auok190x_issue_cmd(par, cmd); | 184 | auok190x_issue_cmd(par, cmd); |
185 | 185 | ||
186 | for (i = 0; i < argc; i++) | 186 | for (i = 0; i < argc; i++) |
187 | auok190x_issue_data(par, argv[i]); | 187 | auok190x_issue_data(par, argv[i]); |
188 | 188 | ||
189 | auok190x_issue_pixels(par, size, data); | 189 | auok190x_issue_pixels(par, size, data); |
190 | 190 | ||
191 | par->board->set_ctl(par, AUOK190X_I80_CS, 1); | 191 | par->board->set_ctl(par, AUOK190X_I80_CS, 1); |
192 | } | 192 | } |
193 | EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_pixels_nowait); | 193 | EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_pixels_nowait); |
194 | 194 | ||
195 | int auok190x_send_cmdargs_pixels(struct auok190xfb_par *par, u16 cmd, | 195 | int auok190x_send_cmdargs_pixels(struct auok190xfb_par *par, u16 cmd, |
196 | int argc, u16 *argv, int size, u16 *data) | 196 | int argc, u16 *argv, int size, u16 *data) |
197 | { | 197 | { |
198 | int ret; | 198 | int ret; |
199 | 199 | ||
200 | ret = par->board->wait_for_rdy(par); | 200 | ret = par->board->wait_for_rdy(par); |
201 | if (ret) | 201 | if (ret) |
202 | return ret; | 202 | return ret; |
203 | 203 | ||
204 | auok190x_send_cmdargs_pixels_nowait(par, cmd, argc, argv, size, data); | 204 | auok190x_send_cmdargs_pixels_nowait(par, cmd, argc, argv, size, data); |
205 | 205 | ||
206 | return 0; | 206 | return 0; |
207 | } | 207 | } |
208 | EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_pixels); | 208 | EXPORT_SYMBOL_GPL(auok190x_send_cmdargs_pixels); |
209 | 209 | ||
210 | /* | 210 | /* |
211 | * fbdefio callbacks - common on both controllers. | 211 | * fbdefio callbacks - common on both controllers. |
212 | */ | 212 | */ |
213 | 213 | ||
214 | static void auok190xfb_dpy_first_io(struct fb_info *info) | 214 | static void auok190xfb_dpy_first_io(struct fb_info *info) |
215 | { | 215 | { |
216 | /* tell runtime-pm that we wish to use the device in a short time */ | 216 | /* tell runtime-pm that we wish to use the device in a short time */ |
217 | pm_runtime_get(info->device); | 217 | pm_runtime_get(info->device); |
218 | } | 218 | } |
219 | 219 | ||
220 | /* this is called back from the deferred io workqueue */ | 220 | /* this is called back from the deferred io workqueue */ |
221 | static void auok190xfb_dpy_deferred_io(struct fb_info *info, | 221 | static void auok190xfb_dpy_deferred_io(struct fb_info *info, |
222 | struct list_head *pagelist) | 222 | struct list_head *pagelist) |
223 | { | 223 | { |
224 | struct fb_deferred_io *fbdefio = info->fbdefio; | 224 | struct fb_deferred_io *fbdefio = info->fbdefio; |
225 | struct auok190xfb_par *par = info->par; | 225 | struct auok190xfb_par *par = info->par; |
226 | u16 yres = info->var.yres; | 226 | u16 yres = info->var.yres; |
227 | u16 xres = info->var.xres; | 227 | u16 xres = info->var.xres; |
228 | u16 y1 = 0, h = 0; | 228 | u16 y1 = 0, h = 0; |
229 | int prev_index = -1; | 229 | int prev_index = -1; |
230 | struct page *cur; | 230 | struct page *cur; |
231 | int h_inc; | 231 | int h_inc; |
232 | int threshold; | 232 | int threshold; |
233 | 233 | ||
234 | if (!list_empty(pagelist)) | 234 | if (!list_empty(pagelist)) |
235 | /* the device resume should've been requested through first_io, | 235 | /* the device resume should've been requested through first_io, |
236 | * if the resume did not finish until now, wait for it. | 236 | * if the resume did not finish until now, wait for it. |
237 | */ | 237 | */ |
238 | pm_runtime_barrier(info->device); | 238 | pm_runtime_barrier(info->device); |
239 | else | 239 | else |
240 | /* We reached this via the fsync or some other way. | 240 | /* We reached this via the fsync or some other way. |
241 | * In either case the first_io function did not run, | 241 | * In either case the first_io function did not run, |
242 | * so we runtime_resume the device here synchronously. | 242 | * so we runtime_resume the device here synchronously. |
243 | */ | 243 | */ |
244 | pm_runtime_get_sync(info->device); | 244 | pm_runtime_get_sync(info->device); |
245 | 245 | ||
246 | /* Do a full screen update every n updates to prevent | 246 | /* Do a full screen update every n updates to prevent |
247 | * excessive darkening of the Sipix display. | 247 | * excessive darkening of the Sipix display. |
248 | * If we do this, there is no need to walk the pages. | 248 | * If we do this, there is no need to walk the pages. |
249 | */ | 249 | */ |
250 | if (par->need_refresh(par)) { | 250 | if (par->need_refresh(par)) { |
251 | par->update_all(par); | 251 | par->update_all(par); |
252 | goto out; | 252 | goto out; |
253 | } | 253 | } |
254 | 254 | ||
255 | /* height increment is fixed per page */ | 255 | /* height increment is fixed per page */ |
256 | h_inc = DIV_ROUND_UP(PAGE_SIZE , xres); | 256 | h_inc = DIV_ROUND_UP(PAGE_SIZE , xres); |
257 | 257 | ||
258 | /* calculate number of pages from pixel height */ | 258 | /* calculate number of pages from pixel height */ |
259 | threshold = par->consecutive_threshold / h_inc; | 259 | threshold = par->consecutive_threshold / h_inc; |
260 | if (threshold < 1) | 260 | if (threshold < 1) |
261 | threshold = 1; | 261 | threshold = 1; |
262 | 262 | ||
263 | /* walk the written page list and swizzle the data */ | 263 | /* walk the written page list and swizzle the data */ |
264 | list_for_each_entry(cur, &fbdefio->pagelist, lru) { | 264 | list_for_each_entry(cur, &fbdefio->pagelist, lru) { |
265 | if (prev_index < 0) { | 265 | if (prev_index < 0) { |
266 | /* just starting so assign first page */ | 266 | /* just starting so assign first page */ |
267 | y1 = (cur->index << PAGE_SHIFT) / xres; | 267 | y1 = (cur->index << PAGE_SHIFT) / xres; |
268 | h = h_inc; | 268 | h = h_inc; |
269 | } else if ((cur->index - prev_index) <= threshold) { | 269 | } else if ((cur->index - prev_index) <= threshold) { |
270 | /* page is within our threshold for single updates */ | 270 | /* page is within our threshold for single updates */ |
271 | h += h_inc * (cur->index - prev_index); | 271 | h += h_inc * (cur->index - prev_index); |
272 | } else { | 272 | } else { |
273 | /* page not consecutive, issue previous update first */ | 273 | /* page not consecutive, issue previous update first */ |
274 | par->update_partial(par, y1, y1 + h); | 274 | par->update_partial(par, y1, y1 + h); |
275 | 275 | ||
276 | /* start over with our non consecutive page */ | 276 | /* start over with our non consecutive page */ |
277 | y1 = (cur->index << PAGE_SHIFT) / xres; | 277 | y1 = (cur->index << PAGE_SHIFT) / xres; |
278 | h = h_inc; | 278 | h = h_inc; |
279 | } | 279 | } |
280 | prev_index = cur->index; | 280 | prev_index = cur->index; |
281 | } | 281 | } |
282 | 282 | ||
283 | /* if we still have any pages to update we do so now */ | 283 | /* if we still have any pages to update we do so now */ |
284 | if (h >= yres) | 284 | if (h >= yres) |
285 | /* its a full screen update, just do it */ | 285 | /* its a full screen update, just do it */ |
286 | par->update_all(par); | 286 | par->update_all(par); |
287 | else | 287 | else |
288 | par->update_partial(par, y1, min((u16) (y1 + h), yres)); | 288 | par->update_partial(par, y1, min((u16) (y1 + h), yres)); |
289 | 289 | ||
290 | out: | 290 | out: |
291 | pm_runtime_mark_last_busy(info->device); | 291 | pm_runtime_mark_last_busy(info->device); |
292 | pm_runtime_put_autosuspend(info->device); | 292 | pm_runtime_put_autosuspend(info->device); |
293 | } | 293 | } |
294 | 294 | ||
295 | /* | 295 | /* |
296 | * framebuffer operations | 296 | * framebuffer operations |
297 | */ | 297 | */ |
298 | 298 | ||
299 | /* | 299 | /* |
300 | * this is the slow path from userspace. they can seek and write to | 300 | * this is the slow path from userspace. they can seek and write to |
301 | * the fb. it's inefficient to do anything less than a full screen draw | 301 | * the fb. it's inefficient to do anything less than a full screen draw |
302 | */ | 302 | */ |
303 | static ssize_t auok190xfb_write(struct fb_info *info, const char __user *buf, | 303 | static ssize_t auok190xfb_write(struct fb_info *info, const char __user *buf, |
304 | size_t count, loff_t *ppos) | 304 | size_t count, loff_t *ppos) |
305 | { | 305 | { |
306 | struct auok190xfb_par *par = info->par; | 306 | struct auok190xfb_par *par = info->par; |
307 | unsigned long p = *ppos; | 307 | unsigned long p = *ppos; |
308 | void *dst; | 308 | void *dst; |
309 | int err = 0; | 309 | int err = 0; |
310 | unsigned long total_size; | 310 | unsigned long total_size; |
311 | 311 | ||
312 | if (info->state != FBINFO_STATE_RUNNING) | 312 | if (info->state != FBINFO_STATE_RUNNING) |
313 | return -EPERM; | 313 | return -EPERM; |
314 | 314 | ||
315 | total_size = info->fix.smem_len; | 315 | total_size = info->fix.smem_len; |
316 | 316 | ||
317 | if (p > total_size) | 317 | if (p > total_size) |
318 | return -EFBIG; | 318 | return -EFBIG; |
319 | 319 | ||
320 | if (count > total_size) { | 320 | if (count > total_size) { |
321 | err = -EFBIG; | 321 | err = -EFBIG; |
322 | count = total_size; | 322 | count = total_size; |
323 | } | 323 | } |
324 | 324 | ||
325 | if (count + p > total_size) { | 325 | if (count + p > total_size) { |
326 | if (!err) | 326 | if (!err) |
327 | err = -ENOSPC; | 327 | err = -ENOSPC; |
328 | 328 | ||
329 | count = total_size - p; | 329 | count = total_size - p; |
330 | } | 330 | } |
331 | 331 | ||
332 | dst = (void *)(info->screen_base + p); | 332 | dst = (void *)(info->screen_base + p); |
333 | 333 | ||
334 | if (copy_from_user(dst, buf, count)) | 334 | if (copy_from_user(dst, buf, count)) |
335 | err = -EFAULT; | 335 | err = -EFAULT; |
336 | 336 | ||
337 | if (!err) | 337 | if (!err) |
338 | *ppos += count; | 338 | *ppos += count; |
339 | 339 | ||
340 | par->update_all(par); | 340 | par->update_all(par); |
341 | 341 | ||
342 | return (err) ? err : count; | 342 | return (err) ? err : count; |
343 | } | 343 | } |
344 | 344 | ||
345 | static void auok190xfb_fillrect(struct fb_info *info, | 345 | static void auok190xfb_fillrect(struct fb_info *info, |
346 | const struct fb_fillrect *rect) | 346 | const struct fb_fillrect *rect) |
347 | { | 347 | { |
348 | struct auok190xfb_par *par = info->par; | 348 | struct auok190xfb_par *par = info->par; |
349 | 349 | ||
350 | sys_fillrect(info, rect); | 350 | sys_fillrect(info, rect); |
351 | 351 | ||
352 | par->update_all(par); | 352 | par->update_all(par); |
353 | } | 353 | } |
354 | 354 | ||
355 | static void auok190xfb_copyarea(struct fb_info *info, | 355 | static void auok190xfb_copyarea(struct fb_info *info, |
356 | const struct fb_copyarea *area) | 356 | const struct fb_copyarea *area) |
357 | { | 357 | { |
358 | struct auok190xfb_par *par = info->par; | 358 | struct auok190xfb_par *par = info->par; |
359 | 359 | ||
360 | sys_copyarea(info, area); | 360 | sys_copyarea(info, area); |
361 | 361 | ||
362 | par->update_all(par); | 362 | par->update_all(par); |
363 | } | 363 | } |
364 | 364 | ||
365 | static void auok190xfb_imageblit(struct fb_info *info, | 365 | static void auok190xfb_imageblit(struct fb_info *info, |
366 | const struct fb_image *image) | 366 | const struct fb_image *image) |
367 | { | 367 | { |
368 | struct auok190xfb_par *par = info->par; | 368 | struct auok190xfb_par *par = info->par; |
369 | 369 | ||
370 | sys_imageblit(info, image); | 370 | sys_imageblit(info, image); |
371 | 371 | ||
372 | par->update_all(par); | 372 | par->update_all(par); |
373 | } | 373 | } |
374 | 374 | ||
375 | static int auok190xfb_check_var(struct fb_var_screeninfo *var, | 375 | static int auok190xfb_check_var(struct fb_var_screeninfo *var, |
376 | struct fb_info *info) | 376 | struct fb_info *info) |
377 | { | 377 | { |
378 | if (info->var.xres != var->xres || info->var.yres != var->yres || | 378 | if (info->var.xres != var->xres || info->var.yres != var->yres || |
379 | info->var.xres_virtual != var->xres_virtual || | 379 | info->var.xres_virtual != var->xres_virtual || |
380 | info->var.yres_virtual != var->yres_virtual) { | 380 | info->var.yres_virtual != var->yres_virtual) { |
381 | pr_info("%s: Resolution not supported: X%u x Y%u\n", | 381 | pr_info("%s: Resolution not supported: X%u x Y%u\n", |
382 | __func__, var->xres, var->yres); | 382 | __func__, var->xres, var->yres); |
383 | return -EINVAL; | 383 | return -EINVAL; |
384 | } | 384 | } |
385 | 385 | ||
386 | /* | 386 | /* |
387 | * Memory limit | 387 | * Memory limit |
388 | */ | 388 | */ |
389 | 389 | ||
390 | if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) { | 390 | if ((info->fix.line_length * var->yres_virtual) > info->fix.smem_len) { |
391 | pr_info("%s: Memory Limit requested yres_virtual = %u\n", | 391 | pr_info("%s: Memory Limit requested yres_virtual = %u\n", |
392 | __func__, var->yres_virtual); | 392 | __func__, var->yres_virtual); |
393 | return -ENOMEM; | 393 | return -ENOMEM; |
394 | } | 394 | } |
395 | 395 | ||
396 | return 0; | 396 | return 0; |
397 | } | 397 | } |
398 | 398 | ||
399 | static struct fb_ops auok190xfb_ops = { | 399 | static struct fb_ops auok190xfb_ops = { |
400 | .owner = THIS_MODULE, | 400 | .owner = THIS_MODULE, |
401 | .fb_read = fb_sys_read, | 401 | .fb_read = fb_sys_read, |
402 | .fb_write = auok190xfb_write, | 402 | .fb_write = auok190xfb_write, |
403 | .fb_fillrect = auok190xfb_fillrect, | 403 | .fb_fillrect = auok190xfb_fillrect, |
404 | .fb_copyarea = auok190xfb_copyarea, | 404 | .fb_copyarea = auok190xfb_copyarea, |
405 | .fb_imageblit = auok190xfb_imageblit, | 405 | .fb_imageblit = auok190xfb_imageblit, |
406 | .fb_check_var = auok190xfb_check_var, | 406 | .fb_check_var = auok190xfb_check_var, |
407 | }; | 407 | }; |
408 | 408 | ||
409 | /* | 409 | /* |
410 | * Controller-functions common to both K1900 and K1901 | 410 | * Controller-functions common to both K1900 and K1901 |
411 | */ | 411 | */ |
412 | 412 | ||
413 | static int auok190x_read_temperature(struct auok190xfb_par *par) | 413 | static int auok190x_read_temperature(struct auok190xfb_par *par) |
414 | { | 414 | { |
415 | struct device *dev = par->info->device; | 415 | struct device *dev = par->info->device; |
416 | u16 data[4]; | 416 | u16 data[4]; |
417 | int temp; | 417 | int temp; |
418 | 418 | ||
419 | pm_runtime_get_sync(dev); | 419 | pm_runtime_get_sync(dev); |
420 | 420 | ||
421 | mutex_lock(&(par->io_lock)); | 421 | mutex_lock(&(par->io_lock)); |
422 | 422 | ||
423 | auok190x_read_cmdargs(par, AUOK190X_CMD_READ_VERSION, 4, data); | 423 | auok190x_read_cmdargs(par, AUOK190X_CMD_READ_VERSION, 4, data); |
424 | 424 | ||
425 | mutex_unlock(&(par->io_lock)); | 425 | mutex_unlock(&(par->io_lock)); |
426 | 426 | ||
427 | pm_runtime_mark_last_busy(dev); | 427 | pm_runtime_mark_last_busy(dev); |
428 | pm_runtime_put_autosuspend(dev); | 428 | pm_runtime_put_autosuspend(dev); |
429 | 429 | ||
430 | /* sanitize and split of half-degrees for now */ | 430 | /* sanitize and split of half-degrees for now */ |
431 | temp = ((data[0] & AUOK190X_VERSION_TEMP_MASK) >> 1); | 431 | temp = ((data[0] & AUOK190X_VERSION_TEMP_MASK) >> 1); |
432 | 432 | ||
433 | /* handle positive and negative temperatures */ | 433 | /* handle positive and negative temperatures */ |
434 | if (temp >= 201) | 434 | if (temp >= 201) |
435 | return (255 - temp + 1) * (-1); | 435 | return (255 - temp + 1) * (-1); |
436 | else | 436 | else |
437 | return temp; | 437 | return temp; |
438 | } | 438 | } |
439 | 439 | ||
440 | static void auok190x_identify(struct auok190xfb_par *par) | 440 | static void auok190x_identify(struct auok190xfb_par *par) |
441 | { | 441 | { |
442 | struct device *dev = par->info->device; | 442 | struct device *dev = par->info->device; |
443 | u16 data[4]; | 443 | u16 data[4]; |
444 | 444 | ||
445 | pm_runtime_get_sync(dev); | 445 | pm_runtime_get_sync(dev); |
446 | 446 | ||
447 | mutex_lock(&(par->io_lock)); | 447 | mutex_lock(&(par->io_lock)); |
448 | 448 | ||
449 | auok190x_read_cmdargs(par, AUOK190X_CMD_READ_VERSION, 4, data); | 449 | auok190x_read_cmdargs(par, AUOK190X_CMD_READ_VERSION, 4, data); |
450 | 450 | ||
451 | mutex_unlock(&(par->io_lock)); | 451 | mutex_unlock(&(par->io_lock)); |
452 | 452 | ||
453 | par->epd_type = data[1] & AUOK190X_VERSION_TEMP_MASK; | 453 | par->epd_type = data[1] & AUOK190X_VERSION_TEMP_MASK; |
454 | 454 | ||
455 | par->panel_size_int = AUOK190X_VERSION_SIZE_INT(data[2]); | 455 | par->panel_size_int = AUOK190X_VERSION_SIZE_INT(data[2]); |
456 | par->panel_size_float = AUOK190X_VERSION_SIZE_FLOAT(data[2]); | 456 | par->panel_size_float = AUOK190X_VERSION_SIZE_FLOAT(data[2]); |
457 | par->panel_model = AUOK190X_VERSION_MODEL(data[2]); | 457 | par->panel_model = AUOK190X_VERSION_MODEL(data[2]); |
458 | 458 | ||
459 | par->tcon_version = AUOK190X_VERSION_TCON(data[3]); | 459 | par->tcon_version = AUOK190X_VERSION_TCON(data[3]); |
460 | par->lut_version = AUOK190X_VERSION_LUT(data[3]); | 460 | par->lut_version = AUOK190X_VERSION_LUT(data[3]); |
461 | 461 | ||
462 | dev_dbg(dev, "panel %d.%din, model 0x%x, EPD 0x%x TCON-rev 0x%x, LUT-rev 0x%x", | 462 | dev_dbg(dev, "panel %d.%din, model 0x%x, EPD 0x%x TCON-rev 0x%x, LUT-rev 0x%x", |
463 | par->panel_size_int, par->panel_size_float, par->panel_model, | 463 | par->panel_size_int, par->panel_size_float, par->panel_model, |
464 | par->epd_type, par->tcon_version, par->lut_version); | 464 | par->epd_type, par->tcon_version, par->lut_version); |
465 | 465 | ||
466 | pm_runtime_mark_last_busy(dev); | 466 | pm_runtime_mark_last_busy(dev); |
467 | pm_runtime_put_autosuspend(dev); | 467 | pm_runtime_put_autosuspend(dev); |
468 | } | 468 | } |
469 | 469 | ||
470 | /* | 470 | /* |
471 | * Sysfs functions | 471 | * Sysfs functions |
472 | */ | 472 | */ |
473 | 473 | ||
474 | static ssize_t update_mode_show(struct device *dev, | 474 | static ssize_t update_mode_show(struct device *dev, |
475 | struct device_attribute *attr, char *buf) | 475 | struct device_attribute *attr, char *buf) |
476 | { | 476 | { |
477 | struct fb_info *info = dev_get_drvdata(dev); | 477 | struct fb_info *info = dev_get_drvdata(dev); |
478 | struct auok190xfb_par *par = info->par; | 478 | struct auok190xfb_par *par = info->par; |
479 | 479 | ||
480 | return sprintf(buf, "%d\n", par->update_mode); | 480 | return sprintf(buf, "%d\n", par->update_mode); |
481 | } | 481 | } |
482 | 482 | ||
483 | static ssize_t update_mode_store(struct device *dev, | 483 | static ssize_t update_mode_store(struct device *dev, |
484 | struct device_attribute *attr, | 484 | struct device_attribute *attr, |
485 | const char *buf, size_t count) | 485 | const char *buf, size_t count) |
486 | { | 486 | { |
487 | struct fb_info *info = dev_get_drvdata(dev); | 487 | struct fb_info *info = dev_get_drvdata(dev); |
488 | struct auok190xfb_par *par = info->par; | 488 | struct auok190xfb_par *par = info->par; |
489 | int mode, ret; | 489 | int mode, ret; |
490 | 490 | ||
491 | ret = kstrtoint(buf, 10, &mode); | 491 | ret = kstrtoint(buf, 10, &mode); |
492 | if (ret) | 492 | if (ret) |
493 | return ret; | 493 | return ret; |
494 | 494 | ||
495 | par->update_mode = mode; | 495 | par->update_mode = mode; |
496 | 496 | ||
497 | /* if we enter a better mode, do a full update */ | 497 | /* if we enter a better mode, do a full update */ |
498 | if (par->last_mode > 1 && mode < par->last_mode) | 498 | if (par->last_mode > 1 && mode < par->last_mode) |
499 | par->update_all(par); | 499 | par->update_all(par); |
500 | 500 | ||
501 | return count; | 501 | return count; |
502 | } | 502 | } |
503 | 503 | ||
504 | static ssize_t flash_show(struct device *dev, struct device_attribute *attr, | 504 | static ssize_t flash_show(struct device *dev, struct device_attribute *attr, |
505 | char *buf) | 505 | char *buf) |
506 | { | 506 | { |
507 | struct fb_info *info = dev_get_drvdata(dev); | 507 | struct fb_info *info = dev_get_drvdata(dev); |
508 | struct auok190xfb_par *par = info->par; | 508 | struct auok190xfb_par *par = info->par; |
509 | 509 | ||
510 | return sprintf(buf, "%d\n", par->flash); | 510 | return sprintf(buf, "%d\n", par->flash); |
511 | } | 511 | } |
512 | 512 | ||
513 | static ssize_t flash_store(struct device *dev, struct device_attribute *attr, | 513 | static ssize_t flash_store(struct device *dev, struct device_attribute *attr, |
514 | const char *buf, size_t count) | 514 | const char *buf, size_t count) |
515 | { | 515 | { |
516 | struct fb_info *info = dev_get_drvdata(dev); | 516 | struct fb_info *info = dev_get_drvdata(dev); |
517 | struct auok190xfb_par *par = info->par; | 517 | struct auok190xfb_par *par = info->par; |
518 | int flash, ret; | 518 | int flash, ret; |
519 | 519 | ||
520 | ret = kstrtoint(buf, 10, &flash); | 520 | ret = kstrtoint(buf, 10, &flash); |
521 | if (ret) | 521 | if (ret) |
522 | return ret; | 522 | return ret; |
523 | 523 | ||
524 | if (flash > 0) | 524 | if (flash > 0) |
525 | par->flash = 1; | 525 | par->flash = 1; |
526 | else | 526 | else |
527 | par->flash = 0; | 527 | par->flash = 0; |
528 | 528 | ||
529 | return count; | 529 | return count; |
530 | } | 530 | } |
531 | 531 | ||
532 | static ssize_t temp_show(struct device *dev, struct device_attribute *attr, | 532 | static ssize_t temp_show(struct device *dev, struct device_attribute *attr, |
533 | char *buf) | 533 | char *buf) |
534 | { | 534 | { |
535 | struct fb_info *info = dev_get_drvdata(dev); | 535 | struct fb_info *info = dev_get_drvdata(dev); |
536 | struct auok190xfb_par *par = info->par; | 536 | struct auok190xfb_par *par = info->par; |
537 | int temp; | 537 | int temp; |
538 | 538 | ||
539 | temp = auok190x_read_temperature(par); | 539 | temp = auok190x_read_temperature(par); |
540 | return sprintf(buf, "%d\n", temp); | 540 | return sprintf(buf, "%d\n", temp); |
541 | } | 541 | } |
542 | 542 | ||
543 | static DEVICE_ATTR(update_mode, 0644, update_mode_show, update_mode_store); | 543 | static DEVICE_ATTR(update_mode, 0644, update_mode_show, update_mode_store); |
544 | static DEVICE_ATTR(flash, 0644, flash_show, flash_store); | 544 | static DEVICE_ATTR(flash, 0644, flash_show, flash_store); |
545 | static DEVICE_ATTR(temp, 0644, temp_show, NULL); | 545 | static DEVICE_ATTR(temp, 0644, temp_show, NULL); |
546 | 546 | ||
547 | static struct attribute *auok190x_attributes[] = { | 547 | static struct attribute *auok190x_attributes[] = { |
548 | &dev_attr_update_mode.attr, | 548 | &dev_attr_update_mode.attr, |
549 | &dev_attr_flash.attr, | 549 | &dev_attr_flash.attr, |
550 | &dev_attr_temp.attr, | 550 | &dev_attr_temp.attr, |
551 | NULL | 551 | NULL |
552 | }; | 552 | }; |
553 | 553 | ||
554 | static const struct attribute_group auok190x_attr_group = { | 554 | static const struct attribute_group auok190x_attr_group = { |
555 | .attrs = auok190x_attributes, | 555 | .attrs = auok190x_attributes, |
556 | }; | 556 | }; |
557 | 557 | ||
558 | static int auok190x_power(struct auok190xfb_par *par, bool on) | 558 | static int auok190x_power(struct auok190xfb_par *par, bool on) |
559 | { | 559 | { |
560 | struct auok190x_board *board = par->board; | 560 | struct auok190x_board *board = par->board; |
561 | int ret; | 561 | int ret; |
562 | 562 | ||
563 | if (on) { | 563 | if (on) { |
564 | /* We should maintain POWER up for at least 80ms before set | 564 | /* We should maintain POWER up for at least 80ms before set |
565 | * RST_N and SLP_N to high (TCON spec 20100803_v35 p59) | 565 | * RST_N and SLP_N to high (TCON spec 20100803_v35 p59) |
566 | */ | 566 | */ |
567 | ret = regulator_enable(par->regulator); | 567 | ret = regulator_enable(par->regulator); |
568 | if (ret) | 568 | if (ret) |
569 | return ret; | 569 | return ret; |
570 | 570 | ||
571 | msleep(200); | 571 | msleep(200); |
572 | gpio_set_value(board->gpio_nrst, 1); | 572 | gpio_set_value(board->gpio_nrst, 1); |
573 | gpio_set_value(board->gpio_nsleep, 1); | 573 | gpio_set_value(board->gpio_nsleep, 1); |
574 | msleep(200); | 574 | msleep(200); |
575 | } else { | 575 | } else { |
576 | regulator_disable(par->regulator); | 576 | regulator_disable(par->regulator); |
577 | gpio_set_value(board->gpio_nrst, 0); | 577 | gpio_set_value(board->gpio_nrst, 0); |
578 | gpio_set_value(board->gpio_nsleep, 0); | 578 | gpio_set_value(board->gpio_nsleep, 0); |
579 | } | 579 | } |
580 | 580 | ||
581 | return 0; | 581 | return 0; |
582 | } | 582 | } |
583 | 583 | ||
584 | /* | 584 | /* |
585 | * Recovery - powercycle the controller | 585 | * Recovery - powercycle the controller |
586 | */ | 586 | */ |
587 | 587 | ||
588 | static void auok190x_recover(struct auok190xfb_par *par) | 588 | static void auok190x_recover(struct auok190xfb_par *par) |
589 | { | 589 | { |
590 | auok190x_power(par, 0); | 590 | auok190x_power(par, 0); |
591 | msleep(100); | 591 | msleep(100); |
592 | auok190x_power(par, 1); | 592 | auok190x_power(par, 1); |
593 | 593 | ||
594 | par->init(par); | 594 | par->init(par); |
595 | 595 | ||
596 | /* wait for init to complete */ | 596 | /* wait for init to complete */ |
597 | par->board->wait_for_rdy(par); | 597 | par->board->wait_for_rdy(par); |
598 | } | 598 | } |
599 | 599 | ||
600 | /* | 600 | /* |
601 | * Power-management | 601 | * Power-management |
602 | */ | 602 | */ |
603 | 603 | ||
604 | #ifdef CONFIG_PM | 604 | #ifdef CONFIG_PM |
605 | static int auok190x_runtime_suspend(struct device *dev) | 605 | static int auok190x_runtime_suspend(struct device *dev) |
606 | { | 606 | { |
607 | struct platform_device *pdev = to_platform_device(dev); | 607 | struct platform_device *pdev = to_platform_device(dev); |
608 | struct fb_info *info = platform_get_drvdata(pdev); | 608 | struct fb_info *info = platform_get_drvdata(pdev); |
609 | struct auok190xfb_par *par = info->par; | 609 | struct auok190xfb_par *par = info->par; |
610 | struct auok190x_board *board = par->board; | 610 | struct auok190x_board *board = par->board; |
611 | u16 standby_param; | 611 | u16 standby_param; |
612 | 612 | ||
613 | /* take and keep the lock until we are resumed, as the controller | 613 | /* take and keep the lock until we are resumed, as the controller |
614 | * will never reach the non-busy state when in standby mode | 614 | * will never reach the non-busy state when in standby mode |
615 | */ | 615 | */ |
616 | mutex_lock(&(par->io_lock)); | 616 | mutex_lock(&(par->io_lock)); |
617 | 617 | ||
618 | if (par->standby) { | 618 | if (par->standby) { |
619 | dev_warn(dev, "already in standby, runtime-pm pairing mismatch\n"); | 619 | dev_warn(dev, "already in standby, runtime-pm pairing mismatch\n"); |
620 | mutex_unlock(&(par->io_lock)); | 620 | mutex_unlock(&(par->io_lock)); |
621 | return 0; | 621 | return 0; |
622 | } | 622 | } |
623 | 623 | ||
624 | /* according to runtime_pm.txt runtime_suspend only means, that the | 624 | /* according to runtime_pm.txt runtime_suspend only means, that the |
625 | * device will not process data and will not communicate with the CPU | 625 | * device will not process data and will not communicate with the CPU |
626 | * As we hold the lock, this stays true even without standby | 626 | * As we hold the lock, this stays true even without standby |
627 | */ | 627 | */ |
628 | if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { | 628 | if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { |
629 | dev_dbg(dev, "runtime suspend without standby\n"); | 629 | dev_dbg(dev, "runtime suspend without standby\n"); |
630 | goto finish; | 630 | goto finish; |
631 | } else if (board->quirks & AUOK190X_QUIRK_STANDBYPARAM) { | 631 | } else if (board->quirks & AUOK190X_QUIRK_STANDBYPARAM) { |
632 | /* for some TCON versions STANDBY expects a parameter (0) but | 632 | /* for some TCON versions STANDBY expects a parameter (0) but |
633 | * it seems the real tcon version has to be determined yet. | 633 | * it seems the real tcon version has to be determined yet. |
634 | */ | 634 | */ |
635 | dev_dbg(dev, "runtime suspend with additional empty param\n"); | 635 | dev_dbg(dev, "runtime suspend with additional empty param\n"); |
636 | standby_param = 0; | 636 | standby_param = 0; |
637 | auok190x_send_cmdargs(par, AUOK190X_CMD_STANDBY, 1, | 637 | auok190x_send_cmdargs(par, AUOK190X_CMD_STANDBY, 1, |
638 | &standby_param); | 638 | &standby_param); |
639 | } else { | 639 | } else { |
640 | dev_dbg(dev, "runtime suspend without param\n"); | 640 | dev_dbg(dev, "runtime suspend without param\n"); |
641 | auok190x_send_command(par, AUOK190X_CMD_STANDBY); | 641 | auok190x_send_command(par, AUOK190X_CMD_STANDBY); |
642 | } | 642 | } |
643 | 643 | ||
644 | msleep(64); | 644 | msleep(64); |
645 | 645 | ||
646 | finish: | 646 | finish: |
647 | par->standby = 1; | 647 | par->standby = 1; |
648 | 648 | ||
649 | return 0; | 649 | return 0; |
650 | } | 650 | } |
651 | 651 | ||
652 | static int auok190x_runtime_resume(struct device *dev) | 652 | static int auok190x_runtime_resume(struct device *dev) |
653 | { | 653 | { |
654 | struct platform_device *pdev = to_platform_device(dev); | 654 | struct platform_device *pdev = to_platform_device(dev); |
655 | struct fb_info *info = platform_get_drvdata(pdev); | 655 | struct fb_info *info = platform_get_drvdata(pdev); |
656 | struct auok190xfb_par *par = info->par; | 656 | struct auok190xfb_par *par = info->par; |
657 | struct auok190x_board *board = par->board; | 657 | struct auok190x_board *board = par->board; |
658 | 658 | ||
659 | if (!par->standby) { | 659 | if (!par->standby) { |
660 | dev_warn(dev, "not in standby, runtime-pm pairing mismatch\n"); | 660 | dev_warn(dev, "not in standby, runtime-pm pairing mismatch\n"); |
661 | return 0; | 661 | return 0; |
662 | } | 662 | } |
663 | 663 | ||
664 | if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { | 664 | if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { |
665 | dev_dbg(dev, "runtime resume without standby\n"); | 665 | dev_dbg(dev, "runtime resume without standby\n"); |
666 | } else { | 666 | } else { |
667 | /* when in standby, controller is always busy | 667 | /* when in standby, controller is always busy |
668 | * and only accepts the wakeup command | 668 | * and only accepts the wakeup command |
669 | */ | 669 | */ |
670 | dev_dbg(dev, "runtime resume from standby\n"); | 670 | dev_dbg(dev, "runtime resume from standby\n"); |
671 | auok190x_send_command_nowait(par, AUOK190X_CMD_WAKEUP); | 671 | auok190x_send_command_nowait(par, AUOK190X_CMD_WAKEUP); |
672 | 672 | ||
673 | msleep(160); | 673 | msleep(160); |
674 | 674 | ||
675 | /* wait for the controller to be ready and release the lock */ | 675 | /* wait for the controller to be ready and release the lock */ |
676 | board->wait_for_rdy(par); | 676 | board->wait_for_rdy(par); |
677 | } | 677 | } |
678 | 678 | ||
679 | par->standby = 0; | 679 | par->standby = 0; |
680 | 680 | ||
681 | mutex_unlock(&(par->io_lock)); | 681 | mutex_unlock(&(par->io_lock)); |
682 | 682 | ||
683 | return 0; | 683 | return 0; |
684 | } | 684 | } |
685 | 685 | ||
686 | static int auok190x_suspend(struct device *dev) | 686 | static int auok190x_suspend(struct device *dev) |
687 | { | 687 | { |
688 | struct platform_device *pdev = to_platform_device(dev); | 688 | struct platform_device *pdev = to_platform_device(dev); |
689 | struct fb_info *info = platform_get_drvdata(pdev); | 689 | struct fb_info *info = platform_get_drvdata(pdev); |
690 | struct auok190xfb_par *par = info->par; | 690 | struct auok190xfb_par *par = info->par; |
691 | struct auok190x_board *board = par->board; | 691 | struct auok190x_board *board = par->board; |
692 | int ret; | 692 | int ret; |
693 | 693 | ||
694 | dev_dbg(dev, "suspend\n"); | 694 | dev_dbg(dev, "suspend\n"); |
695 | if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { | 695 | if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { |
696 | /* suspend via powering off the ic */ | 696 | /* suspend via powering off the ic */ |
697 | dev_dbg(dev, "suspend with broken standby\n"); | 697 | dev_dbg(dev, "suspend with broken standby\n"); |
698 | 698 | ||
699 | auok190x_power(par, 0); | 699 | auok190x_power(par, 0); |
700 | } else { | 700 | } else { |
701 | dev_dbg(dev, "suspend using sleep\n"); | 701 | dev_dbg(dev, "suspend using sleep\n"); |
702 | 702 | ||
703 | /* the sleep state can only be entered from the standby state. | 703 | /* the sleep state can only be entered from the standby state. |
704 | * pm_runtime_get_noresume gets called before the suspend call. | 704 | * pm_runtime_get_noresume gets called before the suspend call. |
705 | * So the devices usage count is >0 but it is not necessarily | 705 | * So the devices usage count is >0 but it is not necessarily |
706 | * active. | 706 | * active. |
707 | */ | 707 | */ |
708 | if (!pm_runtime_status_suspended(dev)) { | 708 | if (!pm_runtime_status_suspended(dev)) { |
709 | ret = auok190x_runtime_suspend(dev); | 709 | ret = auok190x_runtime_suspend(dev); |
710 | if (ret < 0) { | 710 | if (ret < 0) { |
711 | dev_err(dev, "auok190x_runtime_suspend failed with %d\n", | 711 | dev_err(dev, "auok190x_runtime_suspend failed with %d\n", |
712 | ret); | 712 | ret); |
713 | return ret; | 713 | return ret; |
714 | } | 714 | } |
715 | par->manual_standby = 1; | 715 | par->manual_standby = 1; |
716 | } | 716 | } |
717 | 717 | ||
718 | gpio_direction_output(board->gpio_nsleep, 0); | 718 | gpio_direction_output(board->gpio_nsleep, 0); |
719 | } | 719 | } |
720 | 720 | ||
721 | msleep(100); | 721 | msleep(100); |
722 | 722 | ||
723 | return 0; | 723 | return 0; |
724 | } | 724 | } |
725 | 725 | ||
726 | static int auok190x_resume(struct device *dev) | 726 | static int auok190x_resume(struct device *dev) |
727 | { | 727 | { |
728 | struct platform_device *pdev = to_platform_device(dev); | 728 | struct platform_device *pdev = to_platform_device(dev); |
729 | struct fb_info *info = platform_get_drvdata(pdev); | 729 | struct fb_info *info = platform_get_drvdata(pdev); |
730 | struct auok190xfb_par *par = info->par; | 730 | struct auok190xfb_par *par = info->par; |
731 | struct auok190x_board *board = par->board; | 731 | struct auok190x_board *board = par->board; |
732 | 732 | ||
733 | dev_dbg(dev, "resume\n"); | 733 | dev_dbg(dev, "resume\n"); |
734 | if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { | 734 | if (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) { |
735 | dev_dbg(dev, "resume with broken standby\n"); | 735 | dev_dbg(dev, "resume with broken standby\n"); |
736 | 736 | ||
737 | auok190x_power(par, 1); | 737 | auok190x_power(par, 1); |
738 | 738 | ||
739 | par->init(par); | 739 | par->init(par); |
740 | } else { | 740 | } else { |
741 | dev_dbg(dev, "resume from sleep\n"); | 741 | dev_dbg(dev, "resume from sleep\n"); |
742 | 742 | ||
743 | /* device should be in runtime suspend when we were suspended | 743 | /* device should be in runtime suspend when we were suspended |
744 | * and pm_runtime_put_sync gets called after this function. | 744 | * and pm_runtime_put_sync gets called after this function. |
745 | * So there is no need to touch the standby mode here at all. | 745 | * So there is no need to touch the standby mode here at all. |
746 | */ | 746 | */ |
747 | gpio_direction_output(board->gpio_nsleep, 1); | 747 | gpio_direction_output(board->gpio_nsleep, 1); |
748 | msleep(100); | 748 | msleep(100); |
749 | 749 | ||
750 | /* an additional init call seems to be necessary after sleep */ | 750 | /* an additional init call seems to be necessary after sleep */ |
751 | auok190x_runtime_resume(dev); | 751 | auok190x_runtime_resume(dev); |
752 | par->init(par); | 752 | par->init(par); |
753 | 753 | ||
754 | /* if we were runtime-suspended before, suspend again*/ | 754 | /* if we were runtime-suspended before, suspend again*/ |
755 | if (!par->manual_standby) | 755 | if (!par->manual_standby) |
756 | auok190x_runtime_suspend(dev); | 756 | auok190x_runtime_suspend(dev); |
757 | else | 757 | else |
758 | par->manual_standby = 0; | 758 | par->manual_standby = 0; |
759 | } | 759 | } |
760 | 760 | ||
761 | return 0; | 761 | return 0; |
762 | } | 762 | } |
763 | #endif | 763 | #endif |
764 | 764 | ||
765 | const struct dev_pm_ops auok190x_pm = { | 765 | const struct dev_pm_ops auok190x_pm = { |
766 | SET_RUNTIME_PM_OPS(auok190x_runtime_suspend, auok190x_runtime_resume, | 766 | SET_RUNTIME_PM_OPS(auok190x_runtime_suspend, auok190x_runtime_resume, |
767 | NULL) | 767 | NULL) |
768 | SET_SYSTEM_SLEEP_PM_OPS(auok190x_suspend, auok190x_resume) | 768 | SET_SYSTEM_SLEEP_PM_OPS(auok190x_suspend, auok190x_resume) |
769 | }; | 769 | }; |
770 | EXPORT_SYMBOL_GPL(auok190x_pm); | 770 | EXPORT_SYMBOL_GPL(auok190x_pm); |
771 | 771 | ||
772 | /* | 772 | /* |
773 | * Common probe and remove code | 773 | * Common probe and remove code |
774 | */ | 774 | */ |
775 | 775 | ||
776 | int __devinit auok190x_common_probe(struct platform_device *pdev, | 776 | int __devinit auok190x_common_probe(struct platform_device *pdev, |
777 | struct auok190x_init_data *init) | 777 | struct auok190x_init_data *init) |
778 | { | 778 | { |
779 | struct auok190x_board *board = init->board; | 779 | struct auok190x_board *board = init->board; |
780 | struct auok190xfb_par *par; | 780 | struct auok190xfb_par *par; |
781 | struct fb_info *info; | 781 | struct fb_info *info; |
782 | struct panel_info *panel; | 782 | struct panel_info *panel; |
783 | int videomemorysize, ret; | 783 | int videomemorysize, ret; |
784 | unsigned char *videomemory; | 784 | unsigned char *videomemory; |
785 | 785 | ||
786 | /* check board contents */ | 786 | /* check board contents */ |
787 | if (!board->init || !board->cleanup || !board->wait_for_rdy | 787 | if (!board->init || !board->cleanup || !board->wait_for_rdy |
788 | || !board->set_ctl || !board->set_hdb || !board->get_hdb | 788 | || !board->set_ctl || !board->set_hdb || !board->get_hdb |
789 | || !board->setup_irq) | 789 | || !board->setup_irq) |
790 | return -EINVAL; | 790 | return -EINVAL; |
791 | 791 | ||
792 | info = framebuffer_alloc(sizeof(struct auok190xfb_par), &pdev->dev); | 792 | info = framebuffer_alloc(sizeof(struct auok190xfb_par), &pdev->dev); |
793 | if (!info) | 793 | if (!info) |
794 | return -ENOMEM; | 794 | return -ENOMEM; |
795 | 795 | ||
796 | par = info->par; | 796 | par = info->par; |
797 | par->info = info; | 797 | par->info = info; |
798 | par->board = board; | 798 | par->board = board; |
799 | par->recover = auok190x_recover; | 799 | par->recover = auok190x_recover; |
800 | par->update_partial = init->update_partial; | 800 | par->update_partial = init->update_partial; |
801 | par->update_all = init->update_all; | 801 | par->update_all = init->update_all; |
802 | par->need_refresh = init->need_refresh; | 802 | par->need_refresh = init->need_refresh; |
803 | par->init = init->init; | 803 | par->init = init->init; |
804 | 804 | ||
805 | /* init update modes */ | 805 | /* init update modes */ |
806 | par->update_cnt = 0; | 806 | par->update_cnt = 0; |
807 | par->update_mode = -1; | 807 | par->update_mode = -1; |
808 | par->last_mode = -1; | 808 | par->last_mode = -1; |
809 | par->flash = 0; | 809 | par->flash = 0; |
810 | 810 | ||
811 | par->regulator = regulator_get(info->device, "vdd"); | 811 | par->regulator = regulator_get(info->device, "vdd"); |
812 | if (IS_ERR(par->regulator)) { | 812 | if (IS_ERR(par->regulator)) { |
813 | ret = PTR_ERR(par->regulator); | 813 | ret = PTR_ERR(par->regulator); |
814 | dev_err(info->device, "Failed to get regulator: %d\n", ret); | 814 | dev_err(info->device, "Failed to get regulator: %d\n", ret); |
815 | goto err_reg; | 815 | goto err_reg; |
816 | } | 816 | } |
817 | 817 | ||
818 | ret = board->init(par); | 818 | ret = board->init(par); |
819 | if (ret) { | 819 | if (ret) { |
820 | dev_err(info->device, "board init failed, %d\n", ret); | 820 | dev_err(info->device, "board init failed, %d\n", ret); |
821 | goto err_board; | 821 | goto err_board; |
822 | } | 822 | } |
823 | 823 | ||
824 | ret = gpio_request(board->gpio_nsleep, "AUOK190x sleep"); | 824 | ret = gpio_request(board->gpio_nsleep, "AUOK190x sleep"); |
825 | if (ret) { | 825 | if (ret) { |
826 | dev_err(info->device, "could not request sleep gpio, %d\n", | 826 | dev_err(info->device, "could not request sleep gpio, %d\n", |
827 | ret); | 827 | ret); |
828 | goto err_gpio1; | 828 | goto err_gpio1; |
829 | } | 829 | } |
830 | 830 | ||
831 | ret = gpio_direction_output(board->gpio_nsleep, 0); | 831 | ret = gpio_direction_output(board->gpio_nsleep, 0); |
832 | if (ret) { | 832 | if (ret) { |
833 | dev_err(info->device, "could not set sleep gpio, %d\n", ret); | 833 | dev_err(info->device, "could not set sleep gpio, %d\n", ret); |
834 | goto err_gpio2; | 834 | goto err_gpio2; |
835 | } | 835 | } |
836 | 836 | ||
837 | ret = gpio_request(board->gpio_nrst, "AUOK190x reset"); | 837 | ret = gpio_request(board->gpio_nrst, "AUOK190x reset"); |
838 | if (ret) { | 838 | if (ret) { |
839 | dev_err(info->device, "could not request reset gpio, %d\n", | 839 | dev_err(info->device, "could not request reset gpio, %d\n", |
840 | ret); | 840 | ret); |
841 | goto err_gpio2; | 841 | goto err_gpio2; |
842 | } | 842 | } |
843 | 843 | ||
844 | ret = gpio_direction_output(board->gpio_nrst, 0); | 844 | ret = gpio_direction_output(board->gpio_nrst, 0); |
845 | if (ret) { | 845 | if (ret) { |
846 | dev_err(info->device, "could not set reset gpio, %d\n", ret); | 846 | dev_err(info->device, "could not set reset gpio, %d\n", ret); |
847 | goto err_gpio3; | 847 | goto err_gpio3; |
848 | } | 848 | } |
849 | 849 | ||
850 | ret = auok190x_power(par, 1); | 850 | ret = auok190x_power(par, 1); |
851 | if (ret) { | 851 | if (ret) { |
852 | dev_err(info->device, "could not power on the device, %d\n", | 852 | dev_err(info->device, "could not power on the device, %d\n", |
853 | ret); | 853 | ret); |
854 | goto err_gpio3; | 854 | goto err_gpio3; |
855 | } | 855 | } |
856 | 856 | ||
857 | mutex_init(&par->io_lock); | 857 | mutex_init(&par->io_lock); |
858 | 858 | ||
859 | init_waitqueue_head(&par->waitq); | 859 | init_waitqueue_head(&par->waitq); |
860 | 860 | ||
861 | ret = par->board->setup_irq(par->info); | 861 | ret = par->board->setup_irq(par->info); |
862 | if (ret) { | 862 | if (ret) { |
863 | dev_err(info->device, "could not setup ready-irq, %d\n", ret); | 863 | dev_err(info->device, "could not setup ready-irq, %d\n", ret); |
864 | goto err_irq; | 864 | goto err_irq; |
865 | } | 865 | } |
866 | 866 | ||
867 | /* wait for init to complete */ | 867 | /* wait for init to complete */ |
868 | par->board->wait_for_rdy(par); | 868 | par->board->wait_for_rdy(par); |
869 | 869 | ||
870 | /* | 870 | /* |
871 | * From here on the controller can talk to us | 871 | * From here on the controller can talk to us |
872 | */ | 872 | */ |
873 | 873 | ||
874 | /* initialise fix, var, resolution and rotation */ | 874 | /* initialise fix, var, resolution and rotation */ |
875 | 875 | ||
876 | strlcpy(info->fix.id, init->id, 16); | 876 | strlcpy(info->fix.id, init->id, 16); |
877 | info->fix.type = FB_TYPE_PACKED_PIXELS; | 877 | info->fix.type = FB_TYPE_PACKED_PIXELS; |
878 | info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; | 878 | info->fix.visual = FB_VISUAL_STATIC_PSEUDOCOLOR; |
879 | info->fix.xpanstep = 0; | 879 | info->fix.xpanstep = 0; |
880 | info->fix.ypanstep = 0; | 880 | info->fix.ypanstep = 0; |
881 | info->fix.ywrapstep = 0; | 881 | info->fix.ywrapstep = 0; |
882 | info->fix.accel = FB_ACCEL_NONE; | 882 | info->fix.accel = FB_ACCEL_NONE; |
883 | 883 | ||
884 | info->var.bits_per_pixel = 8; | 884 | info->var.bits_per_pixel = 8; |
885 | info->var.grayscale = 1; | 885 | info->var.grayscale = 1; |
886 | info->var.red.length = 8; | 886 | info->var.red.length = 8; |
887 | info->var.green.length = 8; | 887 | info->var.green.length = 8; |
888 | info->var.blue.length = 8; | 888 | info->var.blue.length = 8; |
889 | 889 | ||
890 | panel = &panel_table[board->resolution]; | 890 | panel = &panel_table[board->resolution]; |
891 | 891 | ||
892 | /* if 90 degree rotation, switch width and height */ | 892 | /* if 90 degree rotation, switch width and height */ |
893 | if (board->rotation & 1) { | 893 | if (board->rotation & 1) { |
894 | info->var.xres = panel->h; | 894 | info->var.xres = panel->h; |
895 | info->var.yres = panel->w; | 895 | info->var.yres = panel->w; |
896 | info->var.xres_virtual = panel->h; | 896 | info->var.xres_virtual = panel->h; |
897 | info->var.yres_virtual = panel->w; | 897 | info->var.yres_virtual = panel->w; |
898 | info->fix.line_length = panel->h; | 898 | info->fix.line_length = panel->h; |
899 | } else { | 899 | } else { |
900 | info->var.xres = panel->w; | 900 | info->var.xres = panel->w; |
901 | info->var.yres = panel->h; | 901 | info->var.yres = panel->h; |
902 | info->var.xres_virtual = panel->w; | 902 | info->var.xres_virtual = panel->w; |
903 | info->var.yres_virtual = panel->h; | 903 | info->var.yres_virtual = panel->h; |
904 | info->fix.line_length = panel->w; | 904 | info->fix.line_length = panel->w; |
905 | } | 905 | } |
906 | 906 | ||
907 | par->resolution = board->resolution; | 907 | par->resolution = board->resolution; |
908 | par->rotation = board->rotation; | 908 | par->rotation = board->rotation; |
909 | 909 | ||
910 | /* videomemory handling */ | 910 | /* videomemory handling */ |
911 | 911 | ||
912 | videomemorysize = roundup((panel->w * panel->h), PAGE_SIZE); | 912 | videomemorysize = roundup((panel->w * panel->h), PAGE_SIZE); |
913 | videomemory = vmalloc(videomemorysize); | 913 | videomemory = vmalloc(videomemorysize); |
914 | if (!videomemory) { | 914 | if (!videomemory) { |
915 | ret = -ENOMEM; | 915 | ret = -ENOMEM; |
916 | goto err_irq; | 916 | goto err_irq; |
917 | } | 917 | } |
918 | 918 | ||
919 | memset(videomemory, 0, videomemorysize); | 919 | memset(videomemory, 0, videomemorysize); |
920 | info->screen_base = (char *)videomemory; | 920 | info->screen_base = (char *)videomemory; |
921 | info->fix.smem_len = videomemorysize; | 921 | info->fix.smem_len = videomemorysize; |
922 | 922 | ||
923 | info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; | 923 | info->flags = FBINFO_FLAG_DEFAULT | FBINFO_VIRTFB; |
924 | info->fbops = &auok190xfb_ops; | 924 | info->fbops = &auok190xfb_ops; |
925 | 925 | ||
926 | /* deferred io init */ | 926 | /* deferred io init */ |
927 | 927 | ||
928 | info->fbdefio = devm_kzalloc(info->device, | 928 | info->fbdefio = devm_kzalloc(info->device, |
929 | sizeof(struct fb_deferred_io), | 929 | sizeof(struct fb_deferred_io), |
930 | GFP_KERNEL); | 930 | GFP_KERNEL); |
931 | if (!info->fbdefio) { | 931 | if (!info->fbdefio) { |
932 | dev_err(info->device, "Failed to allocate memory\n"); | 932 | dev_err(info->device, "Failed to allocate memory\n"); |
933 | ret = -ENOMEM; | 933 | ret = -ENOMEM; |
934 | goto err_defio; | 934 | goto err_defio; |
935 | } | 935 | } |
936 | 936 | ||
937 | dev_dbg(info->device, "targetting %d frames per second\n", board->fps); | 937 | dev_dbg(info->device, "targetting %d frames per second\n", board->fps); |
938 | info->fbdefio->delay = HZ / board->fps; | 938 | info->fbdefio->delay = HZ / board->fps; |
939 | info->fbdefio->first_io = auok190xfb_dpy_first_io, | 939 | info->fbdefio->first_io = auok190xfb_dpy_first_io, |
940 | info->fbdefio->deferred_io = auok190xfb_dpy_deferred_io, | 940 | info->fbdefio->deferred_io = auok190xfb_dpy_deferred_io, |
941 | fb_deferred_io_init(info); | 941 | fb_deferred_io_init(info); |
942 | 942 | ||
943 | /* color map */ | 943 | /* color map */ |
944 | 944 | ||
945 | ret = fb_alloc_cmap(&info->cmap, 256, 0); | 945 | ret = fb_alloc_cmap(&info->cmap, 256, 0); |
946 | if (ret < 0) { | 946 | if (ret < 0) { |
947 | dev_err(info->device, "Failed to allocate colormap\n"); | 947 | dev_err(info->device, "Failed to allocate colormap\n"); |
948 | goto err_cmap; | 948 | goto err_cmap; |
949 | } | 949 | } |
950 | 950 | ||
951 | /* controller init */ | 951 | /* controller init */ |
952 | 952 | ||
953 | par->consecutive_threshold = 100; | 953 | par->consecutive_threshold = 100; |
954 | par->init(par); | 954 | par->init(par); |
955 | auok190x_identify(par); | 955 | auok190x_identify(par); |
956 | 956 | ||
957 | platform_set_drvdata(pdev, info); | 957 | platform_set_drvdata(pdev, info); |
958 | 958 | ||
959 | ret = register_framebuffer(info); | 959 | ret = register_framebuffer(info); |
960 | if (ret < 0) | 960 | if (ret < 0) |
961 | goto err_regfb; | 961 | goto err_regfb; |
962 | 962 | ||
963 | ret = sysfs_create_group(&info->device->kobj, &auok190x_attr_group); | 963 | ret = sysfs_create_group(&info->device->kobj, &auok190x_attr_group); |
964 | if (ret) | 964 | if (ret) |
965 | goto err_sysfs; | 965 | goto err_sysfs; |
966 | 966 | ||
967 | dev_info(info->device, "fb%d: %dx%d using %dK of video memory\n", | 967 | dev_info(info->device, "fb%d: %dx%d using %dK of video memory\n", |
968 | info->node, info->var.xres, info->var.yres, | 968 | info->node, info->var.xres, info->var.yres, |
969 | videomemorysize >> 10); | 969 | videomemorysize >> 10); |
970 | 970 | ||
971 | /* increase autosuspend_delay when we use alternative methods | 971 | /* increase autosuspend_delay when we use alternative methods |
972 | * for runtime_pm | 972 | * for runtime_pm |
973 | */ | 973 | */ |
974 | par->autosuspend_delay = (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) | 974 | par->autosuspend_delay = (board->quirks & AUOK190X_QUIRK_STANDBYBROKEN) |
975 | ? 1000 : 200; | 975 | ? 1000 : 200; |
976 | 976 | ||
977 | pm_runtime_set_active(info->device); | 977 | pm_runtime_set_active(info->device); |
978 | pm_runtime_enable(info->device); | 978 | pm_runtime_enable(info->device); |
979 | pm_runtime_set_autosuspend_delay(info->device, par->autosuspend_delay); | 979 | pm_runtime_set_autosuspend_delay(info->device, par->autosuspend_delay); |
980 | pm_runtime_use_autosuspend(info->device); | 980 | pm_runtime_use_autosuspend(info->device); |
981 | 981 | ||
982 | return 0; | 982 | return 0; |
983 | 983 | ||
984 | err_sysfs: | 984 | err_sysfs: |
985 | unregister_framebuffer(info); | 985 | unregister_framebuffer(info); |
986 | err_regfb: | 986 | err_regfb: |
987 | fb_dealloc_cmap(&info->cmap); | 987 | fb_dealloc_cmap(&info->cmap); |
988 | err_cmap: | 988 | err_cmap: |
989 | fb_deferred_io_cleanup(info); | 989 | fb_deferred_io_cleanup(info); |
990 | kfree(info->fbdefio); | ||
991 | err_defio: | 990 | err_defio: |
992 | vfree((void *)info->screen_base); | 991 | vfree((void *)info->screen_base); |
993 | err_irq: | 992 | err_irq: |
994 | auok190x_power(par, 0); | 993 | auok190x_power(par, 0); |
995 | err_gpio3: | 994 | err_gpio3: |
996 | gpio_free(board->gpio_nrst); | 995 | gpio_free(board->gpio_nrst); |
997 | err_gpio2: | 996 | err_gpio2: |
998 | gpio_free(board->gpio_nsleep); | 997 | gpio_free(board->gpio_nsleep); |
999 | err_gpio1: | 998 | err_gpio1: |
1000 | board->cleanup(par); | 999 | board->cleanup(par); |
1001 | err_board: | 1000 | err_board: |
1002 | regulator_put(par->regulator); | 1001 | regulator_put(par->regulator); |
1003 | err_reg: | 1002 | err_reg: |
1004 | framebuffer_release(info); | 1003 | framebuffer_release(info); |
1005 | 1004 | ||
1006 | return ret; | 1005 | return ret; |
1007 | } | 1006 | } |
1008 | EXPORT_SYMBOL_GPL(auok190x_common_probe); | 1007 | EXPORT_SYMBOL_GPL(auok190x_common_probe); |
1009 | 1008 | ||
1010 | int __devexit auok190x_common_remove(struct platform_device *pdev) | 1009 | int __devexit auok190x_common_remove(struct platform_device *pdev) |
1011 | { | 1010 | { |
1012 | struct fb_info *info = platform_get_drvdata(pdev); | 1011 | struct fb_info *info = platform_get_drvdata(pdev); |
1013 | struct auok190xfb_par *par = info->par; | 1012 | struct auok190xfb_par *par = info->par; |
1014 | struct auok190x_board *board = par->board; | 1013 | struct auok190x_board *board = par->board; |
1015 | 1014 | ||
1016 | pm_runtime_disable(info->device); | 1015 | pm_runtime_disable(info->device); |
1017 | 1016 | ||
1018 | sysfs_remove_group(&info->device->kobj, &auok190x_attr_group); | 1017 | sysfs_remove_group(&info->device->kobj, &auok190x_attr_group); |
1019 | 1018 | ||
1020 | unregister_framebuffer(info); | 1019 | unregister_framebuffer(info); |
1021 | 1020 | ||
1022 | fb_dealloc_cmap(&info->cmap); | 1021 | fb_dealloc_cmap(&info->cmap); |
1023 | 1022 | ||
1024 | fb_deferred_io_cleanup(info); | 1023 | fb_deferred_io_cleanup(info); |
1025 | kfree(info->fbdefio); | ||
1026 | 1024 | ||
1027 | vfree((void *)info->screen_base); | 1025 | vfree((void *)info->screen_base); |
1028 | 1026 | ||
1029 | auok190x_power(par, 0); | 1027 | auok190x_power(par, 0); |
1030 | 1028 | ||
1031 | gpio_free(board->gpio_nrst); | 1029 | gpio_free(board->gpio_nrst); |
1032 | gpio_free(board->gpio_nsleep); | 1030 | gpio_free(board->gpio_nsleep); |
1033 | 1031 | ||
1034 | board->cleanup(par); | 1032 | board->cleanup(par); |
1035 | 1033 | ||
1036 | regulator_put(par->regulator); | 1034 | regulator_put(par->regulator); |
1037 | 1035 | ||
1038 | framebuffer_release(info); | 1036 | framebuffer_release(info); |
1039 | 1037 | ||
1040 | return 0; | 1038 | return 0; |
1041 | } | 1039 | } |
1042 | EXPORT_SYMBOL_GPL(auok190x_common_remove); | 1040 | EXPORT_SYMBOL_GPL(auok190x_common_remove); |
1043 | 1041 | ||
1044 | MODULE_DESCRIPTION("Common code for AUO-K190X controllers"); | 1042 | MODULE_DESCRIPTION("Common code for AUO-K190X controllers"); |
1045 | MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); | 1043 | MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>"); |
1046 | MODULE_LICENSE("GPL"); | 1044 | MODULE_LICENSE("GPL"); |
1047 | 1045 |
drivers/video/console/bitblit.c
1 | /* | 1 | /* |
2 | * linux/drivers/video/console/bitblit.c -- BitBlitting Operation | 2 | * linux/drivers/video/console/bitblit.c -- BitBlitting Operation |
3 | * | 3 | * |
4 | * Originally from the 'accel_*' routines in drivers/video/console/fbcon.c | 4 | * Originally from the 'accel_*' routines in drivers/video/console/fbcon.c |
5 | * | 5 | * |
6 | * Copyright (C) 2004 Antonino Daplas <adaplas @pol.net> | 6 | * Copyright (C) 2004 Antonino Daplas <adaplas @pol.net> |
7 | * | 7 | * |
8 | * This file is subject to the terms and conditions of the GNU General Public | 8 | * This file is subject to the terms and conditions of the GNU General Public |
9 | * License. See the file COPYING in the main directory of this archive for | 9 | * License. See the file COPYING in the main directory of this archive for |
10 | * more details. | 10 | * more details. |
11 | */ | 11 | */ |
12 | 12 | ||
13 | #include <linux/module.h> | 13 | #include <linux/module.h> |
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/string.h> | 15 | #include <linux/string.h> |
16 | #include <linux/fb.h> | 16 | #include <linux/fb.h> |
17 | #include <linux/vt_kern.h> | 17 | #include <linux/vt_kern.h> |
18 | #include <linux/console.h> | 18 | #include <linux/console.h> |
19 | #include <asm/types.h> | 19 | #include <asm/types.h> |
20 | #include "fbcon.h" | 20 | #include "fbcon.h" |
21 | 21 | ||
22 | /* | 22 | /* |
23 | * Accelerated handlers. | 23 | * Accelerated handlers. |
24 | */ | 24 | */ |
25 | static void update_attr(u8 *dst, u8 *src, int attribute, | 25 | static void update_attr(u8 *dst, u8 *src, int attribute, |
26 | struct vc_data *vc) | 26 | struct vc_data *vc) |
27 | { | 27 | { |
28 | int i, offset = (vc->vc_font.height < 10) ? 1 : 2; | 28 | int i, offset = (vc->vc_font.height < 10) ? 1 : 2; |
29 | int width = DIV_ROUND_UP(vc->vc_font.width, 8); | 29 | int width = DIV_ROUND_UP(vc->vc_font.width, 8); |
30 | unsigned int cellsize = vc->vc_font.height * width; | 30 | unsigned int cellsize = vc->vc_font.height * width; |
31 | u8 c; | 31 | u8 c; |
32 | 32 | ||
33 | offset = cellsize - (offset * width); | 33 | offset = cellsize - (offset * width); |
34 | for (i = 0; i < cellsize; i++) { | 34 | for (i = 0; i < cellsize; i++) { |
35 | c = src[i]; | 35 | c = src[i]; |
36 | if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i >= offset) | 36 | if (attribute & FBCON_ATTRIBUTE_UNDERLINE && i >= offset) |
37 | c = 0xff; | 37 | c = 0xff; |
38 | if (attribute & FBCON_ATTRIBUTE_BOLD) | 38 | if (attribute & FBCON_ATTRIBUTE_BOLD) |
39 | c |= c >> 1; | 39 | c |= c >> 1; |
40 | if (attribute & FBCON_ATTRIBUTE_REVERSE) | 40 | if (attribute & FBCON_ATTRIBUTE_REVERSE) |
41 | c = ~c; | 41 | c = ~c; |
42 | dst[i] = c; | 42 | dst[i] = c; |
43 | } | 43 | } |
44 | } | 44 | } |
45 | 45 | ||
46 | static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy, | 46 | static void bit_bmove(struct vc_data *vc, struct fb_info *info, int sy, |
47 | int sx, int dy, int dx, int height, int width) | 47 | int sx, int dy, int dx, int height, int width) |
48 | { | 48 | { |
49 | struct fb_copyarea area; | 49 | struct fb_copyarea area; |
50 | 50 | ||
51 | area.sx = sx * vc->vc_font.width; | 51 | area.sx = sx * vc->vc_font.width; |
52 | area.sy = sy * vc->vc_font.height; | 52 | area.sy = sy * vc->vc_font.height; |
53 | area.dx = dx * vc->vc_font.width; | 53 | area.dx = dx * vc->vc_font.width; |
54 | area.dy = dy * vc->vc_font.height; | 54 | area.dy = dy * vc->vc_font.height; |
55 | area.height = height * vc->vc_font.height; | 55 | area.height = height * vc->vc_font.height; |
56 | area.width = width * vc->vc_font.width; | 56 | area.width = width * vc->vc_font.width; |
57 | 57 | ||
58 | info->fbops->fb_copyarea(info, &area); | 58 | info->fbops->fb_copyarea(info, &area); |
59 | } | 59 | } |
60 | 60 | ||
61 | static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy, | 61 | static void bit_clear(struct vc_data *vc, struct fb_info *info, int sy, |
62 | int sx, int height, int width) | 62 | int sx, int height, int width) |
63 | { | 63 | { |
64 | int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; | 64 | int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; |
65 | struct fb_fillrect region; | 65 | struct fb_fillrect region; |
66 | 66 | ||
67 | region.color = attr_bgcol_ec(bgshift, vc, info); | 67 | region.color = attr_bgcol_ec(bgshift, vc, info); |
68 | region.dx = sx * vc->vc_font.width; | 68 | region.dx = sx * vc->vc_font.width; |
69 | region.dy = sy * vc->vc_font.height; | 69 | region.dy = sy * vc->vc_font.height; |
70 | region.width = width * vc->vc_font.width; | 70 | region.width = width * vc->vc_font.width; |
71 | region.height = height * vc->vc_font.height; | 71 | region.height = height * vc->vc_font.height; |
72 | region.rop = ROP_COPY; | 72 | region.rop = ROP_COPY; |
73 | 73 | ||
74 | info->fbops->fb_fillrect(info, ®ion); | 74 | info->fbops->fb_fillrect(info, ®ion); |
75 | } | 75 | } |
76 | 76 | ||
77 | static inline void bit_putcs_aligned(struct vc_data *vc, struct fb_info *info, | 77 | static inline void bit_putcs_aligned(struct vc_data *vc, struct fb_info *info, |
78 | const u16 *s, u32 attr, u32 cnt, | 78 | const u16 *s, u32 attr, u32 cnt, |
79 | u32 d_pitch, u32 s_pitch, u32 cellsize, | 79 | u32 d_pitch, u32 s_pitch, u32 cellsize, |
80 | struct fb_image *image, u8 *buf, u8 *dst) | 80 | struct fb_image *image, u8 *buf, u8 *dst) |
81 | { | 81 | { |
82 | u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; | 82 | u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; |
83 | u32 idx = vc->vc_font.width >> 3; | 83 | u32 idx = vc->vc_font.width >> 3; |
84 | u8 *src; | 84 | u8 *src; |
85 | 85 | ||
86 | while (cnt--) { | 86 | while (cnt--) { |
87 | src = vc->vc_font.data + (scr_readw(s++)& | 87 | src = vc->vc_font.data + (scr_readw(s++)& |
88 | charmask)*cellsize; | 88 | charmask)*cellsize; |
89 | 89 | ||
90 | if (attr) { | 90 | if (attr) { |
91 | update_attr(buf, src, attr, vc); | 91 | update_attr(buf, src, attr, vc); |
92 | src = buf; | 92 | src = buf; |
93 | } | 93 | } |
94 | 94 | ||
95 | if (likely(idx == 1)) | 95 | if (likely(idx == 1)) |
96 | __fb_pad_aligned_buffer(dst, d_pitch, src, idx, | 96 | __fb_pad_aligned_buffer(dst, d_pitch, src, idx, |
97 | image->height); | 97 | image->height); |
98 | else | 98 | else |
99 | fb_pad_aligned_buffer(dst, d_pitch, src, idx, | 99 | fb_pad_aligned_buffer(dst, d_pitch, src, idx, |
100 | image->height); | 100 | image->height); |
101 | 101 | ||
102 | dst += s_pitch; | 102 | dst += s_pitch; |
103 | } | 103 | } |
104 | 104 | ||
105 | info->fbops->fb_imageblit(info, image); | 105 | info->fbops->fb_imageblit(info, image); |
106 | } | 106 | } |
107 | 107 | ||
108 | static inline void bit_putcs_unaligned(struct vc_data *vc, | 108 | static inline void bit_putcs_unaligned(struct vc_data *vc, |
109 | struct fb_info *info, const u16 *s, | 109 | struct fb_info *info, const u16 *s, |
110 | u32 attr, u32 cnt, u32 d_pitch, | 110 | u32 attr, u32 cnt, u32 d_pitch, |
111 | u32 s_pitch, u32 cellsize, | 111 | u32 s_pitch, u32 cellsize, |
112 | struct fb_image *image, u8 *buf, | 112 | struct fb_image *image, u8 *buf, |
113 | u8 *dst) | 113 | u8 *dst) |
114 | { | 114 | { |
115 | u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; | 115 | u16 charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; |
116 | u32 shift_low = 0, mod = vc->vc_font.width % 8; | 116 | u32 shift_low = 0, mod = vc->vc_font.width % 8; |
117 | u32 shift_high = 8; | 117 | u32 shift_high = 8; |
118 | u32 idx = vc->vc_font.width >> 3; | 118 | u32 idx = vc->vc_font.width >> 3; |
119 | u8 *src; | 119 | u8 *src; |
120 | 120 | ||
121 | while (cnt--) { | 121 | while (cnt--) { |
122 | src = vc->vc_font.data + (scr_readw(s++)& | 122 | src = vc->vc_font.data + (scr_readw(s++)& |
123 | charmask)*cellsize; | 123 | charmask)*cellsize; |
124 | 124 | ||
125 | if (attr) { | 125 | if (attr) { |
126 | update_attr(buf, src, attr, vc); | 126 | update_attr(buf, src, attr, vc); |
127 | src = buf; | 127 | src = buf; |
128 | } | 128 | } |
129 | 129 | ||
130 | fb_pad_unaligned_buffer(dst, d_pitch, src, idx, | 130 | fb_pad_unaligned_buffer(dst, d_pitch, src, idx, |
131 | image->height, shift_high, | 131 | image->height, shift_high, |
132 | shift_low, mod); | 132 | shift_low, mod); |
133 | shift_low += mod; | 133 | shift_low += mod; |
134 | dst += (shift_low >= 8) ? s_pitch : s_pitch - 1; | 134 | dst += (shift_low >= 8) ? s_pitch : s_pitch - 1; |
135 | shift_low &= 7; | 135 | shift_low &= 7; |
136 | shift_high = 8 - shift_low; | 136 | shift_high = 8 - shift_low; |
137 | } | 137 | } |
138 | 138 | ||
139 | info->fbops->fb_imageblit(info, image); | 139 | info->fbops->fb_imageblit(info, image); |
140 | 140 | ||
141 | } | 141 | } |
142 | 142 | ||
143 | static void bit_putcs(struct vc_data *vc, struct fb_info *info, | 143 | static void bit_putcs(struct vc_data *vc, struct fb_info *info, |
144 | const unsigned short *s, int count, int yy, int xx, | 144 | const unsigned short *s, int count, int yy, int xx, |
145 | int fg, int bg) | 145 | int fg, int bg) |
146 | { | 146 | { |
147 | struct fb_image image; | 147 | struct fb_image image; |
148 | u32 width = DIV_ROUND_UP(vc->vc_font.width, 8); | 148 | u32 width = DIV_ROUND_UP(vc->vc_font.width, 8); |
149 | u32 cellsize = width * vc->vc_font.height; | 149 | u32 cellsize = width * vc->vc_font.height; |
150 | u32 maxcnt = info->pixmap.size/cellsize; | 150 | u32 maxcnt = info->pixmap.size/cellsize; |
151 | u32 scan_align = info->pixmap.scan_align - 1; | 151 | u32 scan_align = info->pixmap.scan_align - 1; |
152 | u32 buf_align = info->pixmap.buf_align - 1; | 152 | u32 buf_align = info->pixmap.buf_align - 1; |
153 | u32 mod = vc->vc_font.width % 8, cnt, pitch, size; | 153 | u32 mod = vc->vc_font.width % 8, cnt, pitch, size; |
154 | u32 attribute = get_attribute(info, scr_readw(s)); | 154 | u32 attribute = get_attribute(info, scr_readw(s)); |
155 | u8 *dst, *buf = NULL; | 155 | u8 *dst, *buf = NULL; |
156 | 156 | ||
157 | image.fg_color = fg; | 157 | image.fg_color = fg; |
158 | image.bg_color = bg; | 158 | image.bg_color = bg; |
159 | image.dx = xx * vc->vc_font.width; | 159 | image.dx = xx * vc->vc_font.width; |
160 | image.dy = yy * vc->vc_font.height; | 160 | image.dy = yy * vc->vc_font.height; |
161 | image.height = vc->vc_font.height; | 161 | image.height = vc->vc_font.height; |
162 | image.depth = 1; | 162 | image.depth = 1; |
163 | 163 | ||
164 | if (attribute) { | 164 | if (attribute) { |
165 | buf = kmalloc(cellsize, GFP_KERNEL); | 165 | buf = kmalloc(cellsize, GFP_ATOMIC); |
166 | if (!buf) | 166 | if (!buf) |
167 | return; | 167 | return; |
168 | } | 168 | } |
169 | 169 | ||
170 | while (count) { | 170 | while (count) { |
171 | if (count > maxcnt) | 171 | if (count > maxcnt) |
172 | cnt = maxcnt; | 172 | cnt = maxcnt; |
173 | else | 173 | else |
174 | cnt = count; | 174 | cnt = count; |
175 | 175 | ||
176 | image.width = vc->vc_font.width * cnt; | 176 | image.width = vc->vc_font.width * cnt; |
177 | pitch = DIV_ROUND_UP(image.width, 8) + scan_align; | 177 | pitch = DIV_ROUND_UP(image.width, 8) + scan_align; |
178 | pitch &= ~scan_align; | 178 | pitch &= ~scan_align; |
179 | size = pitch * image.height + buf_align; | 179 | size = pitch * image.height + buf_align; |
180 | size &= ~buf_align; | 180 | size &= ~buf_align; |
181 | dst = fb_get_buffer_offset(info, &info->pixmap, size); | 181 | dst = fb_get_buffer_offset(info, &info->pixmap, size); |
182 | image.data = dst; | 182 | image.data = dst; |
183 | 183 | ||
184 | if (!mod) | 184 | if (!mod) |
185 | bit_putcs_aligned(vc, info, s, attribute, cnt, pitch, | 185 | bit_putcs_aligned(vc, info, s, attribute, cnt, pitch, |
186 | width, cellsize, &image, buf, dst); | 186 | width, cellsize, &image, buf, dst); |
187 | else | 187 | else |
188 | bit_putcs_unaligned(vc, info, s, attribute, cnt, | 188 | bit_putcs_unaligned(vc, info, s, attribute, cnt, |
189 | pitch, width, cellsize, &image, | 189 | pitch, width, cellsize, &image, |
190 | buf, dst); | 190 | buf, dst); |
191 | 191 | ||
192 | image.dx += cnt * vc->vc_font.width; | 192 | image.dx += cnt * vc->vc_font.width; |
193 | count -= cnt; | 193 | count -= cnt; |
194 | s += cnt; | 194 | s += cnt; |
195 | } | 195 | } |
196 | 196 | ||
197 | /* buf is always NULL except when in monochrome mode, so in this case | 197 | /* buf is always NULL except when in monochrome mode, so in this case |
198 | it's a gain to check buf against NULL even though kfree() handles | 198 | it's a gain to check buf against NULL even though kfree() handles |
199 | NULL pointers just fine */ | 199 | NULL pointers just fine */ |
200 | if (unlikely(buf)) | 200 | if (unlikely(buf)) |
201 | kfree(buf); | 201 | kfree(buf); |
202 | 202 | ||
203 | } | 203 | } |
204 | 204 | ||
205 | static void bit_clear_margins(struct vc_data *vc, struct fb_info *info, | 205 | static void bit_clear_margins(struct vc_data *vc, struct fb_info *info, |
206 | int bottom_only) | 206 | int bottom_only) |
207 | { | 207 | { |
208 | int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; | 208 | int bgshift = (vc->vc_hi_font_mask) ? 13 : 12; |
209 | unsigned int cw = vc->vc_font.width; | 209 | unsigned int cw = vc->vc_font.width; |
210 | unsigned int ch = vc->vc_font.height; | 210 | unsigned int ch = vc->vc_font.height; |
211 | unsigned int rw = info->var.xres - (vc->vc_cols*cw); | 211 | unsigned int rw = info->var.xres - (vc->vc_cols*cw); |
212 | unsigned int bh = info->var.yres - (vc->vc_rows*ch); | 212 | unsigned int bh = info->var.yres - (vc->vc_rows*ch); |
213 | unsigned int rs = info->var.xres - rw; | 213 | unsigned int rs = info->var.xres - rw; |
214 | unsigned int bs = info->var.yres - bh; | 214 | unsigned int bs = info->var.yres - bh; |
215 | struct fb_fillrect region; | 215 | struct fb_fillrect region; |
216 | 216 | ||
217 | region.color = attr_bgcol_ec(bgshift, vc, info); | 217 | region.color = attr_bgcol_ec(bgshift, vc, info); |
218 | region.rop = ROP_COPY; | 218 | region.rop = ROP_COPY; |
219 | 219 | ||
220 | if (rw && !bottom_only) { | 220 | if (rw && !bottom_only) { |
221 | region.dx = info->var.xoffset + rs; | 221 | region.dx = info->var.xoffset + rs; |
222 | region.dy = 0; | 222 | region.dy = 0; |
223 | region.width = rw; | 223 | region.width = rw; |
224 | region.height = info->var.yres_virtual; | 224 | region.height = info->var.yres_virtual; |
225 | info->fbops->fb_fillrect(info, ®ion); | 225 | info->fbops->fb_fillrect(info, ®ion); |
226 | } | 226 | } |
227 | 227 | ||
228 | if (bh) { | 228 | if (bh) { |
229 | region.dx = info->var.xoffset; | 229 | region.dx = info->var.xoffset; |
230 | region.dy = info->var.yoffset + bs; | 230 | region.dy = info->var.yoffset + bs; |
231 | region.width = rs; | 231 | region.width = rs; |
232 | region.height = bh; | 232 | region.height = bh; |
233 | info->fbops->fb_fillrect(info, ®ion); | 233 | info->fbops->fb_fillrect(info, ®ion); |
234 | } | 234 | } |
235 | } | 235 | } |
236 | 236 | ||
237 | static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode, | 237 | static void bit_cursor(struct vc_data *vc, struct fb_info *info, int mode, |
238 | int softback_lines, int fg, int bg) | 238 | int softback_lines, int fg, int bg) |
239 | { | 239 | { |
240 | struct fb_cursor cursor; | 240 | struct fb_cursor cursor; |
241 | struct fbcon_ops *ops = info->fbcon_par; | 241 | struct fbcon_ops *ops = info->fbcon_par; |
242 | unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; | 242 | unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; |
243 | int w = DIV_ROUND_UP(vc->vc_font.width, 8), c; | 243 | int w = DIV_ROUND_UP(vc->vc_font.width, 8), c; |
244 | int y = real_y(ops->p, vc->vc_y); | 244 | int y = real_y(ops->p, vc->vc_y); |
245 | int attribute, use_sw = (vc->vc_cursor_type & 0x10); | 245 | int attribute, use_sw = (vc->vc_cursor_type & 0x10); |
246 | int err = 1; | 246 | int err = 1; |
247 | char *src; | 247 | char *src; |
248 | 248 | ||
249 | cursor.set = 0; | 249 | cursor.set = 0; |
250 | 250 | ||
251 | if (softback_lines) { | 251 | if (softback_lines) { |
252 | if (y + softback_lines >= vc->vc_rows) { | 252 | if (y + softback_lines >= vc->vc_rows) { |
253 | mode = CM_ERASE; | 253 | mode = CM_ERASE; |
254 | ops->cursor_flash = 0; | 254 | ops->cursor_flash = 0; |
255 | return; | 255 | return; |
256 | } else | 256 | } else |
257 | y += softback_lines; | 257 | y += softback_lines; |
258 | } | 258 | } |
259 | 259 | ||
260 | c = scr_readw((u16 *) vc->vc_pos); | 260 | c = scr_readw((u16 *) vc->vc_pos); |
261 | attribute = get_attribute(info, c); | 261 | attribute = get_attribute(info, c); |
262 | src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height)); | 262 | src = vc->vc_font.data + ((c & charmask) * (w * vc->vc_font.height)); |
263 | 263 | ||
264 | if (ops->cursor_state.image.data != src || | 264 | if (ops->cursor_state.image.data != src || |
265 | ops->cursor_reset) { | 265 | ops->cursor_reset) { |
266 | ops->cursor_state.image.data = src; | 266 | ops->cursor_state.image.data = src; |
267 | cursor.set |= FB_CUR_SETIMAGE; | 267 | cursor.set |= FB_CUR_SETIMAGE; |
268 | } | 268 | } |
269 | 269 | ||
270 | if (attribute) { | 270 | if (attribute) { |
271 | u8 *dst; | 271 | u8 *dst; |
272 | 272 | ||
273 | dst = kmalloc(w * vc->vc_font.height, GFP_ATOMIC); | 273 | dst = kmalloc(w * vc->vc_font.height, GFP_ATOMIC); |
274 | if (!dst) | 274 | if (!dst) |
275 | return; | 275 | return; |
276 | kfree(ops->cursor_data); | 276 | kfree(ops->cursor_data); |
277 | ops->cursor_data = dst; | 277 | ops->cursor_data = dst; |
278 | update_attr(dst, src, attribute, vc); | 278 | update_attr(dst, src, attribute, vc); |
279 | src = dst; | 279 | src = dst; |
280 | } | 280 | } |
281 | 281 | ||
282 | if (ops->cursor_state.image.fg_color != fg || | 282 | if (ops->cursor_state.image.fg_color != fg || |
283 | ops->cursor_state.image.bg_color != bg || | 283 | ops->cursor_state.image.bg_color != bg || |
284 | ops->cursor_reset) { | 284 | ops->cursor_reset) { |
285 | ops->cursor_state.image.fg_color = fg; | 285 | ops->cursor_state.image.fg_color = fg; |
286 | ops->cursor_state.image.bg_color = bg; | 286 | ops->cursor_state.image.bg_color = bg; |
287 | cursor.set |= FB_CUR_SETCMAP; | 287 | cursor.set |= FB_CUR_SETCMAP; |
288 | } | 288 | } |
289 | 289 | ||
290 | if ((ops->cursor_state.image.dx != (vc->vc_font.width * vc->vc_x)) || | 290 | if ((ops->cursor_state.image.dx != (vc->vc_font.width * vc->vc_x)) || |
291 | (ops->cursor_state.image.dy != (vc->vc_font.height * y)) || | 291 | (ops->cursor_state.image.dy != (vc->vc_font.height * y)) || |
292 | ops->cursor_reset) { | 292 | ops->cursor_reset) { |
293 | ops->cursor_state.image.dx = vc->vc_font.width * vc->vc_x; | 293 | ops->cursor_state.image.dx = vc->vc_font.width * vc->vc_x; |
294 | ops->cursor_state.image.dy = vc->vc_font.height * y; | 294 | ops->cursor_state.image.dy = vc->vc_font.height * y; |
295 | cursor.set |= FB_CUR_SETPOS; | 295 | cursor.set |= FB_CUR_SETPOS; |
296 | } | 296 | } |
297 | 297 | ||
298 | if (ops->cursor_state.image.height != vc->vc_font.height || | 298 | if (ops->cursor_state.image.height != vc->vc_font.height || |
299 | ops->cursor_state.image.width != vc->vc_font.width || | 299 | ops->cursor_state.image.width != vc->vc_font.width || |
300 | ops->cursor_reset) { | 300 | ops->cursor_reset) { |
301 | ops->cursor_state.image.height = vc->vc_font.height; | 301 | ops->cursor_state.image.height = vc->vc_font.height; |
302 | ops->cursor_state.image.width = vc->vc_font.width; | 302 | ops->cursor_state.image.width = vc->vc_font.width; |
303 | cursor.set |= FB_CUR_SETSIZE; | 303 | cursor.set |= FB_CUR_SETSIZE; |
304 | } | 304 | } |
305 | 305 | ||
306 | if (ops->cursor_state.hot.x || ops->cursor_state.hot.y || | 306 | if (ops->cursor_state.hot.x || ops->cursor_state.hot.y || |
307 | ops->cursor_reset) { | 307 | ops->cursor_reset) { |
308 | ops->cursor_state.hot.x = cursor.hot.y = 0; | 308 | ops->cursor_state.hot.x = cursor.hot.y = 0; |
309 | cursor.set |= FB_CUR_SETHOT; | 309 | cursor.set |= FB_CUR_SETHOT; |
310 | } | 310 | } |
311 | 311 | ||
312 | if (cursor.set & FB_CUR_SETSIZE || | 312 | if (cursor.set & FB_CUR_SETSIZE || |
313 | vc->vc_cursor_type != ops->p->cursor_shape || | 313 | vc->vc_cursor_type != ops->p->cursor_shape || |
314 | ops->cursor_state.mask == NULL || | 314 | ops->cursor_state.mask == NULL || |
315 | ops->cursor_reset) { | 315 | ops->cursor_reset) { |
316 | char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC); | 316 | char *mask = kmalloc(w*vc->vc_font.height, GFP_ATOMIC); |
317 | int cur_height, size, i = 0; | 317 | int cur_height, size, i = 0; |
318 | u8 msk = 0xff; | 318 | u8 msk = 0xff; |
319 | 319 | ||
320 | if (!mask) | 320 | if (!mask) |
321 | return; | 321 | return; |
322 | 322 | ||
323 | kfree(ops->cursor_state.mask); | 323 | kfree(ops->cursor_state.mask); |
324 | ops->cursor_state.mask = mask; | 324 | ops->cursor_state.mask = mask; |
325 | 325 | ||
326 | ops->p->cursor_shape = vc->vc_cursor_type; | 326 | ops->p->cursor_shape = vc->vc_cursor_type; |
327 | cursor.set |= FB_CUR_SETSHAPE; | 327 | cursor.set |= FB_CUR_SETSHAPE; |
328 | 328 | ||
329 | switch (ops->p->cursor_shape & CUR_HWMASK) { | 329 | switch (ops->p->cursor_shape & CUR_HWMASK) { |
330 | case CUR_NONE: | 330 | case CUR_NONE: |
331 | cur_height = 0; | 331 | cur_height = 0; |
332 | break; | 332 | break; |
333 | case CUR_UNDERLINE: | 333 | case CUR_UNDERLINE: |
334 | cur_height = (vc->vc_font.height < 10) ? 1 : 2; | 334 | cur_height = (vc->vc_font.height < 10) ? 1 : 2; |
335 | break; | 335 | break; |
336 | case CUR_LOWER_THIRD: | 336 | case CUR_LOWER_THIRD: |
337 | cur_height = vc->vc_font.height/3; | 337 | cur_height = vc->vc_font.height/3; |
338 | break; | 338 | break; |
339 | case CUR_LOWER_HALF: | 339 | case CUR_LOWER_HALF: |
340 | cur_height = vc->vc_font.height >> 1; | 340 | cur_height = vc->vc_font.height >> 1; |
341 | break; | 341 | break; |
342 | case CUR_TWO_THIRDS: | 342 | case CUR_TWO_THIRDS: |
343 | cur_height = (vc->vc_font.height << 1)/3; | 343 | cur_height = (vc->vc_font.height << 1)/3; |
344 | break; | 344 | break; |
345 | case CUR_BLOCK: | 345 | case CUR_BLOCK: |
346 | default: | 346 | default: |
347 | cur_height = vc->vc_font.height; | 347 | cur_height = vc->vc_font.height; |
348 | break; | 348 | break; |
349 | } | 349 | } |
350 | size = (vc->vc_font.height - cur_height) * w; | 350 | size = (vc->vc_font.height - cur_height) * w; |
351 | while (size--) | 351 | while (size--) |
352 | mask[i++] = ~msk; | 352 | mask[i++] = ~msk; |
353 | size = cur_height * w; | 353 | size = cur_height * w; |
354 | while (size--) | 354 | while (size--) |
355 | mask[i++] = msk; | 355 | mask[i++] = msk; |
356 | } | 356 | } |
357 | 357 | ||
358 | switch (mode) { | 358 | switch (mode) { |
359 | case CM_ERASE: | 359 | case CM_ERASE: |
360 | ops->cursor_state.enable = 0; | 360 | ops->cursor_state.enable = 0; |
361 | break; | 361 | break; |
362 | case CM_DRAW: | 362 | case CM_DRAW: |
363 | case CM_MOVE: | 363 | case CM_MOVE: |
364 | default: | 364 | default: |
365 | ops->cursor_state.enable = (use_sw) ? 0 : 1; | 365 | ops->cursor_state.enable = (use_sw) ? 0 : 1; |
366 | break; | 366 | break; |
367 | } | 367 | } |
368 | 368 | ||
369 | cursor.image.data = src; | 369 | cursor.image.data = src; |
370 | cursor.image.fg_color = ops->cursor_state.image.fg_color; | 370 | cursor.image.fg_color = ops->cursor_state.image.fg_color; |
371 | cursor.image.bg_color = ops->cursor_state.image.bg_color; | 371 | cursor.image.bg_color = ops->cursor_state.image.bg_color; |
372 | cursor.image.dx = ops->cursor_state.image.dx; | 372 | cursor.image.dx = ops->cursor_state.image.dx; |
373 | cursor.image.dy = ops->cursor_state.image.dy; | 373 | cursor.image.dy = ops->cursor_state.image.dy; |
374 | cursor.image.height = ops->cursor_state.image.height; | 374 | cursor.image.height = ops->cursor_state.image.height; |
375 | cursor.image.width = ops->cursor_state.image.width; | 375 | cursor.image.width = ops->cursor_state.image.width; |
376 | cursor.hot.x = ops->cursor_state.hot.x; | 376 | cursor.hot.x = ops->cursor_state.hot.x; |
377 | cursor.hot.y = ops->cursor_state.hot.y; | 377 | cursor.hot.y = ops->cursor_state.hot.y; |
378 | cursor.mask = ops->cursor_state.mask; | 378 | cursor.mask = ops->cursor_state.mask; |
379 | cursor.enable = ops->cursor_state.enable; | 379 | cursor.enable = ops->cursor_state.enable; |
380 | cursor.image.depth = 1; | 380 | cursor.image.depth = 1; |
381 | cursor.rop = ROP_XOR; | 381 | cursor.rop = ROP_XOR; |
382 | 382 | ||
383 | if (info->fbops->fb_cursor) | 383 | if (info->fbops->fb_cursor) |
384 | err = info->fbops->fb_cursor(info, &cursor); | 384 | err = info->fbops->fb_cursor(info, &cursor); |
385 | 385 | ||
386 | if (err) | 386 | if (err) |
387 | soft_cursor(info, &cursor); | 387 | soft_cursor(info, &cursor); |
388 | 388 | ||
389 | ops->cursor_reset = 0; | 389 | ops->cursor_reset = 0; |
390 | } | 390 | } |
391 | 391 | ||
392 | static int bit_update_start(struct fb_info *info) | 392 | static int bit_update_start(struct fb_info *info) |
393 | { | 393 | { |
394 | struct fbcon_ops *ops = info->fbcon_par; | 394 | struct fbcon_ops *ops = info->fbcon_par; |
395 | int err; | 395 | int err; |
396 | 396 | ||
397 | err = fb_pan_display(info, &ops->var); | 397 | err = fb_pan_display(info, &ops->var); |
398 | ops->var.xoffset = info->var.xoffset; | 398 | ops->var.xoffset = info->var.xoffset; |
399 | ops->var.yoffset = info->var.yoffset; | 399 | ops->var.yoffset = info->var.yoffset; |
400 | ops->var.vmode = info->var.vmode; | 400 | ops->var.vmode = info->var.vmode; |
401 | return err; | 401 | return err; |
402 | } | 402 | } |
403 | 403 | ||
404 | void fbcon_set_bitops(struct fbcon_ops *ops) | 404 | void fbcon_set_bitops(struct fbcon_ops *ops) |
405 | { | 405 | { |
406 | ops->bmove = bit_bmove; | 406 | ops->bmove = bit_bmove; |
407 | ops->clear = bit_clear; | 407 | ops->clear = bit_clear; |
408 | ops->putcs = bit_putcs; | 408 | ops->putcs = bit_putcs; |
409 | ops->clear_margins = bit_clear_margins; | 409 | ops->clear_margins = bit_clear_margins; |
410 | ops->cursor = bit_cursor; | 410 | ops->cursor = bit_cursor; |
411 | ops->update_start = bit_update_start; | 411 | ops->update_start = bit_update_start; |
412 | ops->rotate_font = NULL; | 412 | ops->rotate_font = NULL; |
413 | 413 | ||
414 | if (ops->rotate) | 414 | if (ops->rotate) |
415 | fbcon_set_rotate(ops); | 415 | fbcon_set_rotate(ops); |
416 | } | 416 | } |
417 | 417 | ||
418 | EXPORT_SYMBOL(fbcon_set_bitops); | 418 | EXPORT_SYMBOL(fbcon_set_bitops); |
419 | 419 | ||
420 | MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>"); | 420 | MODULE_AUTHOR("Antonino Daplas <adaplas@pol.net>"); |
421 | MODULE_DESCRIPTION("Bit Blitting Operation"); | 421 | MODULE_DESCRIPTION("Bit Blitting Operation"); |
422 | MODULE_LICENSE("GPL"); | 422 | MODULE_LICENSE("GPL"); |
423 | 423 | ||
424 | 424 |
drivers/video/console/fbcon.c
1 | /* | 1 | /* |
2 | * linux/drivers/video/fbcon.c -- Low level frame buffer based console driver | 2 | * linux/drivers/video/fbcon.c -- Low level frame buffer based console driver |
3 | * | 3 | * |
4 | * Copyright (C) 1995 Geert Uytterhoeven | 4 | * Copyright (C) 1995 Geert Uytterhoeven |
5 | * | 5 | * |
6 | * | 6 | * |
7 | * This file is based on the original Amiga console driver (amicon.c): | 7 | * This file is based on the original Amiga console driver (amicon.c): |
8 | * | 8 | * |
9 | * Copyright (C) 1993 Hamish Macdonald | 9 | * Copyright (C) 1993 Hamish Macdonald |
10 | * Greg Harp | 10 | * Greg Harp |
11 | * Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk] | 11 | * Copyright (C) 1994 David Carter [carter@compsci.bristol.ac.uk] |
12 | * | 12 | * |
13 | * with work by William Rucklidge (wjr@cs.cornell.edu) | 13 | * with work by William Rucklidge (wjr@cs.cornell.edu) |
14 | * Geert Uytterhoeven | 14 | * Geert Uytterhoeven |
15 | * Jes Sorensen (jds@kom.auc.dk) | 15 | * Jes Sorensen (jds@kom.auc.dk) |
16 | * Martin Apel | 16 | * Martin Apel |
17 | * | 17 | * |
18 | * and on the original Atari console driver (atacon.c): | 18 | * and on the original Atari console driver (atacon.c): |
19 | * | 19 | * |
20 | * Copyright (C) 1993 Bjoern Brauel | 20 | * Copyright (C) 1993 Bjoern Brauel |
21 | * Roman Hodek | 21 | * Roman Hodek |
22 | * | 22 | * |
23 | * with work by Guenther Kelleter | 23 | * with work by Guenther Kelleter |
24 | * Martin Schaller | 24 | * Martin Schaller |
25 | * Andreas Schwab | 25 | * Andreas Schwab |
26 | * | 26 | * |
27 | * Hardware cursor support added by Emmanuel Marty (core@ggi-project.org) | 27 | * Hardware cursor support added by Emmanuel Marty (core@ggi-project.org) |
28 | * Smart redraw scrolling, arbitrary font width support, 512char font support | 28 | * Smart redraw scrolling, arbitrary font width support, 512char font support |
29 | * and software scrollback added by | 29 | * and software scrollback added by |
30 | * Jakub Jelinek (jj@ultra.linux.cz) | 30 | * Jakub Jelinek (jj@ultra.linux.cz) |
31 | * | 31 | * |
32 | * Random hacking by Martin Mares <mj@ucw.cz> | 32 | * Random hacking by Martin Mares <mj@ucw.cz> |
33 | * | 33 | * |
34 | * 2001 - Documented with DocBook | 34 | * 2001 - Documented with DocBook |
35 | * - Brad Douglas <brad@neruo.com> | 35 | * - Brad Douglas <brad@neruo.com> |
36 | * | 36 | * |
37 | * The low level operations for the various display memory organizations are | 37 | * The low level operations for the various display memory organizations are |
38 | * now in separate source files. | 38 | * now in separate source files. |
39 | * | 39 | * |
40 | * Currently the following organizations are supported: | 40 | * Currently the following organizations are supported: |
41 | * | 41 | * |
42 | * o afb Amiga bitplanes | 42 | * o afb Amiga bitplanes |
43 | * o cfb{2,4,8,16,24,32} Packed pixels | 43 | * o cfb{2,4,8,16,24,32} Packed pixels |
44 | * o ilbm Amiga interleaved bitplanes | 44 | * o ilbm Amiga interleaved bitplanes |
45 | * o iplan2p[248] Atari interleaved bitplanes | 45 | * o iplan2p[248] Atari interleaved bitplanes |
46 | * o mfb Monochrome | 46 | * o mfb Monochrome |
47 | * o vga VGA characters/attributes | 47 | * o vga VGA characters/attributes |
48 | * | 48 | * |
49 | * To do: | 49 | * To do: |
50 | * | 50 | * |
51 | * - Implement 16 plane mode (iplan2p16) | 51 | * - Implement 16 plane mode (iplan2p16) |
52 | * | 52 | * |
53 | * | 53 | * |
54 | * This file is subject to the terms and conditions of the GNU General Public | 54 | * This file is subject to the terms and conditions of the GNU General Public |
55 | * License. See the file COPYING in the main directory of this archive for | 55 | * License. See the file COPYING in the main directory of this archive for |
56 | * more details. | 56 | * more details. |
57 | */ | 57 | */ |
58 | 58 | ||
59 | #undef FBCONDEBUG | 59 | #undef FBCONDEBUG |
60 | 60 | ||
61 | #include <linux/module.h> | 61 | #include <linux/module.h> |
62 | #include <linux/types.h> | 62 | #include <linux/types.h> |
63 | #include <linux/fs.h> | 63 | #include <linux/fs.h> |
64 | #include <linux/kernel.h> | 64 | #include <linux/kernel.h> |
65 | #include <linux/delay.h> /* MSch: for IRQ probe */ | 65 | #include <linux/delay.h> /* MSch: for IRQ probe */ |
66 | #include <linux/console.h> | 66 | #include <linux/console.h> |
67 | #include <linux/string.h> | 67 | #include <linux/string.h> |
68 | #include <linux/kd.h> | 68 | #include <linux/kd.h> |
69 | #include <linux/slab.h> | 69 | #include <linux/slab.h> |
70 | #include <linux/fb.h> | 70 | #include <linux/fb.h> |
71 | #include <linux/vt_kern.h> | 71 | #include <linux/vt_kern.h> |
72 | #include <linux/selection.h> | 72 | #include <linux/selection.h> |
73 | #include <linux/font.h> | 73 | #include <linux/font.h> |
74 | #include <linux/smp.h> | 74 | #include <linux/smp.h> |
75 | #include <linux/init.h> | 75 | #include <linux/init.h> |
76 | #include <linux/interrupt.h> | 76 | #include <linux/interrupt.h> |
77 | #include <linux/crc32.h> /* For counting font checksums */ | 77 | #include <linux/crc32.h> /* For counting font checksums */ |
78 | #include <asm/fb.h> | 78 | #include <asm/fb.h> |
79 | #include <asm/irq.h> | 79 | #include <asm/irq.h> |
80 | 80 | ||
81 | #include "fbcon.h" | 81 | #include "fbcon.h" |
82 | 82 | ||
83 | #ifdef FBCONDEBUG | 83 | #ifdef FBCONDEBUG |
84 | # define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) | 84 | # define DPRINTK(fmt, args...) printk(KERN_DEBUG "%s: " fmt, __func__ , ## args) |
85 | #else | 85 | #else |
86 | # define DPRINTK(fmt, args...) | 86 | # define DPRINTK(fmt, args...) |
87 | #endif | 87 | #endif |
88 | 88 | ||
89 | enum { | 89 | enum { |
90 | FBCON_LOGO_CANSHOW = -1, /* the logo can be shown */ | 90 | FBCON_LOGO_CANSHOW = -1, /* the logo can be shown */ |
91 | FBCON_LOGO_DRAW = -2, /* draw the logo to a console */ | 91 | FBCON_LOGO_DRAW = -2, /* draw the logo to a console */ |
92 | FBCON_LOGO_DONTSHOW = -3 /* do not show the logo */ | 92 | FBCON_LOGO_DONTSHOW = -3 /* do not show the logo */ |
93 | }; | 93 | }; |
94 | 94 | ||
95 | static struct display fb_display[MAX_NR_CONSOLES]; | 95 | static struct display fb_display[MAX_NR_CONSOLES]; |
96 | 96 | ||
97 | static signed char con2fb_map[MAX_NR_CONSOLES]; | 97 | static signed char con2fb_map[MAX_NR_CONSOLES]; |
98 | static signed char con2fb_map_boot[MAX_NR_CONSOLES]; | 98 | static signed char con2fb_map_boot[MAX_NR_CONSOLES]; |
99 | 99 | ||
100 | static int logo_lines; | 100 | static int logo_lines; |
101 | /* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO | 101 | /* logo_shown is an index to vc_cons when >= 0; otherwise follows FBCON_LOGO |
102 | enums. */ | 102 | enums. */ |
103 | static int logo_shown = FBCON_LOGO_CANSHOW; | 103 | static int logo_shown = FBCON_LOGO_CANSHOW; |
104 | /* Software scrollback */ | 104 | /* Software scrollback */ |
105 | static int fbcon_softback_size = 32768; | 105 | static int fbcon_softback_size = 32768; |
106 | static unsigned long softback_buf, softback_curr; | 106 | static unsigned long softback_buf, softback_curr; |
107 | static unsigned long softback_in; | 107 | static unsigned long softback_in; |
108 | static unsigned long softback_top, softback_end; | 108 | static unsigned long softback_top, softback_end; |
109 | static int softback_lines; | 109 | static int softback_lines; |
110 | /* console mappings */ | 110 | /* console mappings */ |
111 | static int first_fb_vc; | 111 | static int first_fb_vc; |
112 | static int last_fb_vc = MAX_NR_CONSOLES - 1; | 112 | static int last_fb_vc = MAX_NR_CONSOLES - 1; |
113 | static int fbcon_is_default = 1; | 113 | static int fbcon_is_default = 1; |
114 | static int fbcon_has_exited; | 114 | static int fbcon_has_exited; |
115 | static int primary_device = -1; | 115 | static int primary_device = -1; |
116 | static int fbcon_has_console_bind; | 116 | static int fbcon_has_console_bind; |
117 | 117 | ||
118 | #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY | 118 | #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY |
119 | static int map_override; | 119 | static int map_override; |
120 | 120 | ||
121 | static inline void fbcon_map_override(void) | 121 | static inline void fbcon_map_override(void) |
122 | { | 122 | { |
123 | map_override = 1; | 123 | map_override = 1; |
124 | } | 124 | } |
125 | #else | 125 | #else |
126 | static inline void fbcon_map_override(void) | 126 | static inline void fbcon_map_override(void) |
127 | { | 127 | { |
128 | } | 128 | } |
129 | #endif /* CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY */ | 129 | #endif /* CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY */ |
130 | 130 | ||
131 | /* font data */ | 131 | /* font data */ |
132 | static char fontname[40]; | 132 | static char fontname[40]; |
133 | 133 | ||
134 | /* current fb_info */ | 134 | /* current fb_info */ |
135 | static int info_idx = -1; | 135 | static int info_idx = -1; |
136 | 136 | ||
137 | /* console rotation */ | 137 | /* console rotation */ |
138 | static int initial_rotation; | 138 | static int initial_rotation; |
139 | static int fbcon_has_sysfs; | 139 | static int fbcon_has_sysfs; |
140 | 140 | ||
141 | static const struct consw fb_con; | 141 | static const struct consw fb_con; |
142 | 142 | ||
143 | #define CM_SOFTBACK (8) | 143 | #define CM_SOFTBACK (8) |
144 | 144 | ||
145 | #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row) | 145 | #define advance_row(p, delta) (unsigned short *)((unsigned long)(p) + (delta) * vc->vc_size_row) |
146 | 146 | ||
147 | static int fbcon_set_origin(struct vc_data *); | 147 | static int fbcon_set_origin(struct vc_data *); |
148 | 148 | ||
149 | #define CURSOR_DRAW_DELAY (1) | 149 | #define CURSOR_DRAW_DELAY (1) |
150 | 150 | ||
151 | static int vbl_cursor_cnt; | 151 | static int vbl_cursor_cnt; |
152 | static int fbcon_cursor_noblink; | 152 | static int fbcon_cursor_noblink; |
153 | 153 | ||
154 | #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1) | 154 | #define divides(a, b) ((!(a) || (b)%(a)) ? 0 : 1) |
155 | 155 | ||
156 | /* | 156 | /* |
157 | * Interface used by the world | 157 | * Interface used by the world |
158 | */ | 158 | */ |
159 | 159 | ||
160 | static const char *fbcon_startup(void); | 160 | static const char *fbcon_startup(void); |
161 | static void fbcon_init(struct vc_data *vc, int init); | 161 | static void fbcon_init(struct vc_data *vc, int init); |
162 | static void fbcon_deinit(struct vc_data *vc); | 162 | static void fbcon_deinit(struct vc_data *vc); |
163 | static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, | 163 | static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, |
164 | int width); | 164 | int width); |
165 | static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos); | 165 | static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos); |
166 | static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, | 166 | static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, |
167 | int count, int ypos, int xpos); | 167 | int count, int ypos, int xpos); |
168 | static void fbcon_clear_margins(struct vc_data *vc, int bottom_only); | 168 | static void fbcon_clear_margins(struct vc_data *vc, int bottom_only); |
169 | static void fbcon_cursor(struct vc_data *vc, int mode); | 169 | static void fbcon_cursor(struct vc_data *vc, int mode); |
170 | static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, | 170 | static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, |
171 | int count); | 171 | int count); |
172 | static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, | 172 | static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, |
173 | int height, int width); | 173 | int height, int width); |
174 | static int fbcon_switch(struct vc_data *vc); | 174 | static int fbcon_switch(struct vc_data *vc); |
175 | static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch); | 175 | static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch); |
176 | static int fbcon_set_palette(struct vc_data *vc, unsigned char *table); | 176 | static int fbcon_set_palette(struct vc_data *vc, unsigned char *table); |
177 | static int fbcon_scrolldelta(struct vc_data *vc, int lines); | 177 | static int fbcon_scrolldelta(struct vc_data *vc, int lines); |
178 | 178 | ||
179 | /* | 179 | /* |
180 | * Internal routines | 180 | * Internal routines |
181 | */ | 181 | */ |
182 | static __inline__ void ywrap_up(struct vc_data *vc, int count); | 182 | static __inline__ void ywrap_up(struct vc_data *vc, int count); |
183 | static __inline__ void ywrap_down(struct vc_data *vc, int count); | 183 | static __inline__ void ywrap_down(struct vc_data *vc, int count); |
184 | static __inline__ void ypan_up(struct vc_data *vc, int count); | 184 | static __inline__ void ypan_up(struct vc_data *vc, int count); |
185 | static __inline__ void ypan_down(struct vc_data *vc, int count); | 185 | static __inline__ void ypan_down(struct vc_data *vc, int count); |
186 | static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx, | 186 | static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx, |
187 | int dy, int dx, int height, int width, u_int y_break); | 187 | int dy, int dx, int height, int width, u_int y_break); |
188 | static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, | 188 | static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, |
189 | int unit); | 189 | int unit); |
190 | static void fbcon_redraw_move(struct vc_data *vc, struct display *p, | 190 | static void fbcon_redraw_move(struct vc_data *vc, struct display *p, |
191 | int line, int count, int dy); | 191 | int line, int count, int dy); |
192 | static void fbcon_modechanged(struct fb_info *info); | 192 | static void fbcon_modechanged(struct fb_info *info); |
193 | static void fbcon_set_all_vcs(struct fb_info *info); | 193 | static void fbcon_set_all_vcs(struct fb_info *info); |
194 | static void fbcon_start(void); | 194 | static void fbcon_start(void); |
195 | static void fbcon_exit(void); | 195 | static void fbcon_exit(void); |
196 | static struct device *fbcon_device; | 196 | static struct device *fbcon_device; |
197 | 197 | ||
198 | #ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION | 198 | #ifdef CONFIG_FRAMEBUFFER_CONSOLE_ROTATION |
199 | static inline void fbcon_set_rotation(struct fb_info *info) | 199 | static inline void fbcon_set_rotation(struct fb_info *info) |
200 | { | 200 | { |
201 | struct fbcon_ops *ops = info->fbcon_par; | 201 | struct fbcon_ops *ops = info->fbcon_par; |
202 | 202 | ||
203 | if (!(info->flags & FBINFO_MISC_TILEBLITTING) && | 203 | if (!(info->flags & FBINFO_MISC_TILEBLITTING) && |
204 | ops->p->con_rotate < 4) | 204 | ops->p->con_rotate < 4) |
205 | ops->rotate = ops->p->con_rotate; | 205 | ops->rotate = ops->p->con_rotate; |
206 | else | 206 | else |
207 | ops->rotate = 0; | 207 | ops->rotate = 0; |
208 | } | 208 | } |
209 | 209 | ||
210 | static void fbcon_rotate(struct fb_info *info, u32 rotate) | 210 | static void fbcon_rotate(struct fb_info *info, u32 rotate) |
211 | { | 211 | { |
212 | struct fbcon_ops *ops= info->fbcon_par; | 212 | struct fbcon_ops *ops= info->fbcon_par; |
213 | struct fb_info *fb_info; | 213 | struct fb_info *fb_info; |
214 | 214 | ||
215 | if (!ops || ops->currcon == -1) | 215 | if (!ops || ops->currcon == -1) |
216 | return; | 216 | return; |
217 | 217 | ||
218 | fb_info = registered_fb[con2fb_map[ops->currcon]]; | 218 | fb_info = registered_fb[con2fb_map[ops->currcon]]; |
219 | 219 | ||
220 | if (info == fb_info) { | 220 | if (info == fb_info) { |
221 | struct display *p = &fb_display[ops->currcon]; | 221 | struct display *p = &fb_display[ops->currcon]; |
222 | 222 | ||
223 | if (rotate < 4) | 223 | if (rotate < 4) |
224 | p->con_rotate = rotate; | 224 | p->con_rotate = rotate; |
225 | else | 225 | else |
226 | p->con_rotate = 0; | 226 | p->con_rotate = 0; |
227 | 227 | ||
228 | fbcon_modechanged(info); | 228 | fbcon_modechanged(info); |
229 | } | 229 | } |
230 | } | 230 | } |
231 | 231 | ||
232 | static void fbcon_rotate_all(struct fb_info *info, u32 rotate) | 232 | static void fbcon_rotate_all(struct fb_info *info, u32 rotate) |
233 | { | 233 | { |
234 | struct fbcon_ops *ops = info->fbcon_par; | 234 | struct fbcon_ops *ops = info->fbcon_par; |
235 | struct vc_data *vc; | 235 | struct vc_data *vc; |
236 | struct display *p; | 236 | struct display *p; |
237 | int i; | 237 | int i; |
238 | 238 | ||
239 | if (!ops || ops->currcon < 0 || rotate > 3) | 239 | if (!ops || ops->currcon < 0 || rotate > 3) |
240 | return; | 240 | return; |
241 | 241 | ||
242 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 242 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
243 | vc = vc_cons[i].d; | 243 | vc = vc_cons[i].d; |
244 | if (!vc || vc->vc_mode != KD_TEXT || | 244 | if (!vc || vc->vc_mode != KD_TEXT || |
245 | registered_fb[con2fb_map[i]] != info) | 245 | registered_fb[con2fb_map[i]] != info) |
246 | continue; | 246 | continue; |
247 | 247 | ||
248 | p = &fb_display[vc->vc_num]; | 248 | p = &fb_display[vc->vc_num]; |
249 | p->con_rotate = rotate; | 249 | p->con_rotate = rotate; |
250 | } | 250 | } |
251 | 251 | ||
252 | fbcon_set_all_vcs(info); | 252 | fbcon_set_all_vcs(info); |
253 | } | 253 | } |
254 | #else | 254 | #else |
255 | static inline void fbcon_set_rotation(struct fb_info *info) | 255 | static inline void fbcon_set_rotation(struct fb_info *info) |
256 | { | 256 | { |
257 | struct fbcon_ops *ops = info->fbcon_par; | 257 | struct fbcon_ops *ops = info->fbcon_par; |
258 | 258 | ||
259 | ops->rotate = FB_ROTATE_UR; | 259 | ops->rotate = FB_ROTATE_UR; |
260 | } | 260 | } |
261 | 261 | ||
262 | static void fbcon_rotate(struct fb_info *info, u32 rotate) | 262 | static void fbcon_rotate(struct fb_info *info, u32 rotate) |
263 | { | 263 | { |
264 | return; | 264 | return; |
265 | } | 265 | } |
266 | 266 | ||
267 | static void fbcon_rotate_all(struct fb_info *info, u32 rotate) | 267 | static void fbcon_rotate_all(struct fb_info *info, u32 rotate) |
268 | { | 268 | { |
269 | return; | 269 | return; |
270 | } | 270 | } |
271 | #endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */ | 271 | #endif /* CONFIG_FRAMEBUFFER_CONSOLE_ROTATION */ |
272 | 272 | ||
273 | static int fbcon_get_rotate(struct fb_info *info) | 273 | static int fbcon_get_rotate(struct fb_info *info) |
274 | { | 274 | { |
275 | struct fbcon_ops *ops = info->fbcon_par; | 275 | struct fbcon_ops *ops = info->fbcon_par; |
276 | 276 | ||
277 | return (ops) ? ops->rotate : 0; | 277 | return (ops) ? ops->rotate : 0; |
278 | } | 278 | } |
279 | 279 | ||
280 | static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info) | 280 | static inline int fbcon_is_inactive(struct vc_data *vc, struct fb_info *info) |
281 | { | 281 | { |
282 | struct fbcon_ops *ops = info->fbcon_par; | 282 | struct fbcon_ops *ops = info->fbcon_par; |
283 | 283 | ||
284 | return (info->state != FBINFO_STATE_RUNNING || | 284 | return (info->state != FBINFO_STATE_RUNNING || |
285 | vc->vc_mode != KD_TEXT || ops->graphics) && | 285 | vc->vc_mode != KD_TEXT || ops->graphics) && |
286 | !vt_force_oops_output(vc); | 286 | !vt_force_oops_output(vc); |
287 | } | 287 | } |
288 | 288 | ||
289 | static int get_color(struct vc_data *vc, struct fb_info *info, | 289 | static int get_color(struct vc_data *vc, struct fb_info *info, |
290 | u16 c, int is_fg) | 290 | u16 c, int is_fg) |
291 | { | 291 | { |
292 | int depth = fb_get_color_depth(&info->var, &info->fix); | 292 | int depth = fb_get_color_depth(&info->var, &info->fix); |
293 | int color = 0; | 293 | int color = 0; |
294 | 294 | ||
295 | if (console_blanked) { | 295 | if (console_blanked) { |
296 | unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; | 296 | unsigned short charmask = vc->vc_hi_font_mask ? 0x1ff : 0xff; |
297 | 297 | ||
298 | c = vc->vc_video_erase_char & charmask; | 298 | c = vc->vc_video_erase_char & charmask; |
299 | } | 299 | } |
300 | 300 | ||
301 | if (depth != 1) | 301 | if (depth != 1) |
302 | color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c) | 302 | color = (is_fg) ? attr_fgcol((vc->vc_hi_font_mask) ? 9 : 8, c) |
303 | : attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c); | 303 | : attr_bgcol((vc->vc_hi_font_mask) ? 13 : 12, c); |
304 | 304 | ||
305 | switch (depth) { | 305 | switch (depth) { |
306 | case 1: | 306 | case 1: |
307 | { | 307 | { |
308 | int col = mono_col(info); | 308 | int col = mono_col(info); |
309 | /* 0 or 1 */ | 309 | /* 0 or 1 */ |
310 | int fg = (info->fix.visual != FB_VISUAL_MONO01) ? col : 0; | 310 | int fg = (info->fix.visual != FB_VISUAL_MONO01) ? col : 0; |
311 | int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : col; | 311 | int bg = (info->fix.visual != FB_VISUAL_MONO01) ? 0 : col; |
312 | 312 | ||
313 | if (console_blanked) | 313 | if (console_blanked) |
314 | fg = bg; | 314 | fg = bg; |
315 | 315 | ||
316 | color = (is_fg) ? fg : bg; | 316 | color = (is_fg) ? fg : bg; |
317 | break; | 317 | break; |
318 | } | 318 | } |
319 | case 2: | 319 | case 2: |
320 | /* | 320 | /* |
321 | * Scale down 16-colors to 4 colors. Default 4-color palette | 321 | * Scale down 16-colors to 4 colors. Default 4-color palette |
322 | * is grayscale. However, simply dividing the values by 4 | 322 | * is grayscale. However, simply dividing the values by 4 |
323 | * will not work, as colors 1, 2 and 3 will be scaled-down | 323 | * will not work, as colors 1, 2 and 3 will be scaled-down |
324 | * to zero rendering them invisible. So empirically convert | 324 | * to zero rendering them invisible. So empirically convert |
325 | * colors to a sane 4-level grayscale. | 325 | * colors to a sane 4-level grayscale. |
326 | */ | 326 | */ |
327 | switch (color) { | 327 | switch (color) { |
328 | case 0: | 328 | case 0: |
329 | color = 0; /* black */ | 329 | color = 0; /* black */ |
330 | break; | 330 | break; |
331 | case 1 ... 6: | 331 | case 1 ... 6: |
332 | color = 2; /* white */ | 332 | color = 2; /* white */ |
333 | break; | 333 | break; |
334 | case 7 ... 8: | 334 | case 7 ... 8: |
335 | color = 1; /* gray */ | 335 | color = 1; /* gray */ |
336 | break; | 336 | break; |
337 | default: | 337 | default: |
338 | color = 3; /* intense white */ | 338 | color = 3; /* intense white */ |
339 | break; | 339 | break; |
340 | } | 340 | } |
341 | break; | 341 | break; |
342 | case 3: | 342 | case 3: |
343 | /* | 343 | /* |
344 | * Last 8 entries of default 16-color palette is a more intense | 344 | * Last 8 entries of default 16-color palette is a more intense |
345 | * version of the first 8 (i.e., same chrominance, different | 345 | * version of the first 8 (i.e., same chrominance, different |
346 | * luminance). | 346 | * luminance). |
347 | */ | 347 | */ |
348 | color &= 7; | 348 | color &= 7; |
349 | break; | 349 | break; |
350 | } | 350 | } |
351 | 351 | ||
352 | 352 | ||
353 | return color; | 353 | return color; |
354 | } | 354 | } |
355 | 355 | ||
356 | static void fbcon_update_softback(struct vc_data *vc) | 356 | static void fbcon_update_softback(struct vc_data *vc) |
357 | { | 357 | { |
358 | int l = fbcon_softback_size / vc->vc_size_row; | 358 | int l = fbcon_softback_size / vc->vc_size_row; |
359 | 359 | ||
360 | if (l > 5) | 360 | if (l > 5) |
361 | softback_end = softback_buf + l * vc->vc_size_row; | 361 | softback_end = softback_buf + l * vc->vc_size_row; |
362 | else | 362 | else |
363 | /* Smaller scrollback makes no sense, and 0 would screw | 363 | /* Smaller scrollback makes no sense, and 0 would screw |
364 | the operation totally */ | 364 | the operation totally */ |
365 | softback_top = 0; | 365 | softback_top = 0; |
366 | } | 366 | } |
367 | 367 | ||
368 | static void fb_flashcursor(struct work_struct *work) | 368 | static void fb_flashcursor(struct work_struct *work) |
369 | { | 369 | { |
370 | struct fb_info *info = container_of(work, struct fb_info, queue); | 370 | struct fb_info *info = container_of(work, struct fb_info, queue); |
371 | struct fbcon_ops *ops = info->fbcon_par; | 371 | struct fbcon_ops *ops = info->fbcon_par; |
372 | struct vc_data *vc = NULL; | 372 | struct vc_data *vc = NULL; |
373 | int c; | 373 | int c; |
374 | int mode; | 374 | int mode; |
375 | int ret; | 375 | int ret; |
376 | 376 | ||
377 | /* FIXME: we should sort out the unbind locking instead */ | 377 | /* FIXME: we should sort out the unbind locking instead */ |
378 | /* instead we just fail to flash the cursor if we can't get | 378 | /* instead we just fail to flash the cursor if we can't get |
379 | * the lock instead of blocking fbcon deinit */ | 379 | * the lock instead of blocking fbcon deinit */ |
380 | ret = console_trylock(); | 380 | ret = console_trylock(); |
381 | if (ret == 0) | 381 | if (ret == 0) |
382 | return; | 382 | return; |
383 | 383 | ||
384 | if (ops && ops->currcon != -1) | 384 | if (ops && ops->currcon != -1) |
385 | vc = vc_cons[ops->currcon].d; | 385 | vc = vc_cons[ops->currcon].d; |
386 | 386 | ||
387 | if (!vc || !CON_IS_VISIBLE(vc) || | 387 | if (!vc || !CON_IS_VISIBLE(vc) || |
388 | registered_fb[con2fb_map[vc->vc_num]] != info || | 388 | registered_fb[con2fb_map[vc->vc_num]] != info || |
389 | vc->vc_deccm != 1) { | 389 | vc->vc_deccm != 1) { |
390 | console_unlock(); | 390 | console_unlock(); |
391 | return; | 391 | return; |
392 | } | 392 | } |
393 | 393 | ||
394 | c = scr_readw((u16 *) vc->vc_pos); | 394 | c = scr_readw((u16 *) vc->vc_pos); |
395 | mode = (!ops->cursor_flash || ops->cursor_state.enable) ? | 395 | mode = (!ops->cursor_flash || ops->cursor_state.enable) ? |
396 | CM_ERASE : CM_DRAW; | 396 | CM_ERASE : CM_DRAW; |
397 | ops->cursor(vc, info, mode, softback_lines, get_color(vc, info, c, 1), | 397 | ops->cursor(vc, info, mode, softback_lines, get_color(vc, info, c, 1), |
398 | get_color(vc, info, c, 0)); | 398 | get_color(vc, info, c, 0)); |
399 | console_unlock(); | 399 | console_unlock(); |
400 | } | 400 | } |
401 | 401 | ||
402 | static void cursor_timer_handler(unsigned long dev_addr) | 402 | static void cursor_timer_handler(unsigned long dev_addr) |
403 | { | 403 | { |
404 | struct fb_info *info = (struct fb_info *) dev_addr; | 404 | struct fb_info *info = (struct fb_info *) dev_addr; |
405 | struct fbcon_ops *ops = info->fbcon_par; | 405 | struct fbcon_ops *ops = info->fbcon_par; |
406 | 406 | ||
407 | schedule_work(&info->queue); | 407 | schedule_work(&info->queue); |
408 | mod_timer(&ops->cursor_timer, jiffies + HZ/5); | 408 | mod_timer(&ops->cursor_timer, jiffies + HZ/5); |
409 | } | 409 | } |
410 | 410 | ||
411 | static void fbcon_add_cursor_timer(struct fb_info *info) | 411 | static void fbcon_add_cursor_timer(struct fb_info *info) |
412 | { | 412 | { |
413 | struct fbcon_ops *ops = info->fbcon_par; | 413 | struct fbcon_ops *ops = info->fbcon_par; |
414 | 414 | ||
415 | if ((!info->queue.func || info->queue.func == fb_flashcursor) && | 415 | if ((!info->queue.func || info->queue.func == fb_flashcursor) && |
416 | !(ops->flags & FBCON_FLAGS_CURSOR_TIMER) && | 416 | !(ops->flags & FBCON_FLAGS_CURSOR_TIMER) && |
417 | !fbcon_cursor_noblink) { | 417 | !fbcon_cursor_noblink) { |
418 | if (!info->queue.func) | 418 | if (!info->queue.func) |
419 | INIT_WORK(&info->queue, fb_flashcursor); | 419 | INIT_WORK(&info->queue, fb_flashcursor); |
420 | 420 | ||
421 | init_timer(&ops->cursor_timer); | 421 | init_timer(&ops->cursor_timer); |
422 | ops->cursor_timer.function = cursor_timer_handler; | 422 | ops->cursor_timer.function = cursor_timer_handler; |
423 | ops->cursor_timer.expires = jiffies + HZ / 5; | 423 | ops->cursor_timer.expires = jiffies + HZ / 5; |
424 | ops->cursor_timer.data = (unsigned long ) info; | 424 | ops->cursor_timer.data = (unsigned long ) info; |
425 | add_timer(&ops->cursor_timer); | 425 | add_timer(&ops->cursor_timer); |
426 | ops->flags |= FBCON_FLAGS_CURSOR_TIMER; | 426 | ops->flags |= FBCON_FLAGS_CURSOR_TIMER; |
427 | } | 427 | } |
428 | } | 428 | } |
429 | 429 | ||
430 | static void fbcon_del_cursor_timer(struct fb_info *info) | 430 | static void fbcon_del_cursor_timer(struct fb_info *info) |
431 | { | 431 | { |
432 | struct fbcon_ops *ops = info->fbcon_par; | 432 | struct fbcon_ops *ops = info->fbcon_par; |
433 | 433 | ||
434 | if (info->queue.func == fb_flashcursor && | 434 | if (info->queue.func == fb_flashcursor && |
435 | ops->flags & FBCON_FLAGS_CURSOR_TIMER) { | 435 | ops->flags & FBCON_FLAGS_CURSOR_TIMER) { |
436 | del_timer_sync(&ops->cursor_timer); | 436 | del_timer_sync(&ops->cursor_timer); |
437 | ops->flags &= ~FBCON_FLAGS_CURSOR_TIMER; | 437 | ops->flags &= ~FBCON_FLAGS_CURSOR_TIMER; |
438 | } | 438 | } |
439 | } | 439 | } |
440 | 440 | ||
441 | #ifndef MODULE | 441 | #ifndef MODULE |
442 | static int __init fb_console_setup(char *this_opt) | 442 | static int __init fb_console_setup(char *this_opt) |
443 | { | 443 | { |
444 | char *options; | 444 | char *options; |
445 | int i, j; | 445 | int i, j; |
446 | 446 | ||
447 | if (!this_opt || !*this_opt) | 447 | if (!this_opt || !*this_opt) |
448 | return 1; | 448 | return 1; |
449 | 449 | ||
450 | while ((options = strsep(&this_opt, ",")) != NULL) { | 450 | while ((options = strsep(&this_opt, ",")) != NULL) { |
451 | if (!strncmp(options, "font:", 5)) | 451 | if (!strncmp(options, "font:", 5)) |
452 | strcpy(fontname, options + 5); | 452 | strlcpy(fontname, options + 5, sizeof(fontname)); |
453 | 453 | ||
454 | if (!strncmp(options, "scrollback:", 11)) { | 454 | if (!strncmp(options, "scrollback:", 11)) { |
455 | options += 11; | 455 | options += 11; |
456 | if (*options) { | 456 | if (*options) { |
457 | fbcon_softback_size = simple_strtoul(options, &options, 0); | 457 | fbcon_softback_size = simple_strtoul(options, &options, 0); |
458 | if (*options == 'k' || *options == 'K') { | 458 | if (*options == 'k' || *options == 'K') { |
459 | fbcon_softback_size *= 1024; | 459 | fbcon_softback_size *= 1024; |
460 | options++; | 460 | options++; |
461 | } | 461 | } |
462 | if (*options != ',') | 462 | if (*options != ',') |
463 | return 1; | 463 | return 1; |
464 | options++; | 464 | options++; |
465 | } else | 465 | } else |
466 | return 1; | 466 | return 1; |
467 | } | 467 | } |
468 | 468 | ||
469 | if (!strncmp(options, "map:", 4)) { | 469 | if (!strncmp(options, "map:", 4)) { |
470 | options += 4; | 470 | options += 4; |
471 | if (*options) { | 471 | if (*options) { |
472 | for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) { | 472 | for (i = 0, j = 0; i < MAX_NR_CONSOLES; i++) { |
473 | if (!options[j]) | 473 | if (!options[j]) |
474 | j = 0; | 474 | j = 0; |
475 | con2fb_map_boot[i] = | 475 | con2fb_map_boot[i] = |
476 | (options[j++]-'0') % FB_MAX; | 476 | (options[j++]-'0') % FB_MAX; |
477 | } | 477 | } |
478 | 478 | ||
479 | fbcon_map_override(); | 479 | fbcon_map_override(); |
480 | } | 480 | } |
481 | 481 | ||
482 | return 1; | 482 | return 1; |
483 | } | 483 | } |
484 | 484 | ||
485 | if (!strncmp(options, "vc:", 3)) { | 485 | if (!strncmp(options, "vc:", 3)) { |
486 | options += 3; | 486 | options += 3; |
487 | if (*options) | 487 | if (*options) |
488 | first_fb_vc = simple_strtoul(options, &options, 10) - 1; | 488 | first_fb_vc = simple_strtoul(options, &options, 10) - 1; |
489 | if (first_fb_vc < 0) | 489 | if (first_fb_vc < 0) |
490 | first_fb_vc = 0; | 490 | first_fb_vc = 0; |
491 | if (*options++ == '-') | 491 | if (*options++ == '-') |
492 | last_fb_vc = simple_strtoul(options, &options, 10) - 1; | 492 | last_fb_vc = simple_strtoul(options, &options, 10) - 1; |
493 | fbcon_is_default = 0; | 493 | fbcon_is_default = 0; |
494 | } | 494 | } |
495 | 495 | ||
496 | if (!strncmp(options, "rotate:", 7)) { | 496 | if (!strncmp(options, "rotate:", 7)) { |
497 | options += 7; | 497 | options += 7; |
498 | if (*options) | 498 | if (*options) |
499 | initial_rotation = simple_strtoul(options, &options, 0); | 499 | initial_rotation = simple_strtoul(options, &options, 0); |
500 | if (initial_rotation > 3) | 500 | if (initial_rotation > 3) |
501 | initial_rotation = 0; | 501 | initial_rotation = 0; |
502 | } | 502 | } |
503 | } | 503 | } |
504 | return 1; | 504 | return 1; |
505 | } | 505 | } |
506 | 506 | ||
507 | __setup("fbcon=", fb_console_setup); | 507 | __setup("fbcon=", fb_console_setup); |
508 | #endif | 508 | #endif |
509 | 509 | ||
510 | static int search_fb_in_map(int idx) | 510 | static int search_fb_in_map(int idx) |
511 | { | 511 | { |
512 | int i, retval = 0; | 512 | int i, retval = 0; |
513 | 513 | ||
514 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 514 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
515 | if (con2fb_map[i] == idx) | 515 | if (con2fb_map[i] == idx) |
516 | retval = 1; | 516 | retval = 1; |
517 | } | 517 | } |
518 | return retval; | 518 | return retval; |
519 | } | 519 | } |
520 | 520 | ||
521 | static int search_for_mapped_con(void) | 521 | static int search_for_mapped_con(void) |
522 | { | 522 | { |
523 | int i, retval = 0; | 523 | int i, retval = 0; |
524 | 524 | ||
525 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 525 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
526 | if (con2fb_map[i] != -1) | 526 | if (con2fb_map[i] != -1) |
527 | retval = 1; | 527 | retval = 1; |
528 | } | 528 | } |
529 | return retval; | 529 | return retval; |
530 | } | 530 | } |
531 | 531 | ||
532 | static int fbcon_takeover(int show_logo) | 532 | static int fbcon_takeover(int show_logo) |
533 | { | 533 | { |
534 | int err, i; | 534 | int err, i; |
535 | 535 | ||
536 | if (!num_registered_fb) | 536 | if (!num_registered_fb) |
537 | return -ENODEV; | 537 | return -ENODEV; |
538 | 538 | ||
539 | if (!show_logo) | 539 | if (!show_logo) |
540 | logo_shown = FBCON_LOGO_DONTSHOW; | 540 | logo_shown = FBCON_LOGO_DONTSHOW; |
541 | 541 | ||
542 | for (i = first_fb_vc; i <= last_fb_vc; i++) | 542 | for (i = first_fb_vc; i <= last_fb_vc; i++) |
543 | con2fb_map[i] = info_idx; | 543 | con2fb_map[i] = info_idx; |
544 | 544 | ||
545 | err = take_over_console(&fb_con, first_fb_vc, last_fb_vc, | 545 | err = take_over_console(&fb_con, first_fb_vc, last_fb_vc, |
546 | fbcon_is_default); | 546 | fbcon_is_default); |
547 | 547 | ||
548 | if (err) { | 548 | if (err) { |
549 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 549 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
550 | con2fb_map[i] = -1; | 550 | con2fb_map[i] = -1; |
551 | } | 551 | } |
552 | info_idx = -1; | 552 | info_idx = -1; |
553 | } else { | 553 | } else { |
554 | fbcon_has_console_bind = 1; | 554 | fbcon_has_console_bind = 1; |
555 | } | 555 | } |
556 | 556 | ||
557 | return err; | 557 | return err; |
558 | } | 558 | } |
559 | 559 | ||
560 | #ifdef MODULE | 560 | #ifdef MODULE |
561 | static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, | 561 | static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, |
562 | int cols, int rows, int new_cols, int new_rows) | 562 | int cols, int rows, int new_cols, int new_rows) |
563 | { | 563 | { |
564 | logo_shown = FBCON_LOGO_DONTSHOW; | 564 | logo_shown = FBCON_LOGO_DONTSHOW; |
565 | } | 565 | } |
566 | #else | 566 | #else |
567 | static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, | 567 | static void fbcon_prepare_logo(struct vc_data *vc, struct fb_info *info, |
568 | int cols, int rows, int new_cols, int new_rows) | 568 | int cols, int rows, int new_cols, int new_rows) |
569 | { | 569 | { |
570 | /* Need to make room for the logo */ | 570 | /* Need to make room for the logo */ |
571 | struct fbcon_ops *ops = info->fbcon_par; | 571 | struct fbcon_ops *ops = info->fbcon_par; |
572 | int cnt, erase = vc->vc_video_erase_char, step; | 572 | int cnt, erase = vc->vc_video_erase_char, step; |
573 | unsigned short *save = NULL, *r, *q; | 573 | unsigned short *save = NULL, *r, *q; |
574 | int logo_height; | 574 | int logo_height; |
575 | 575 | ||
576 | if (info->flags & FBINFO_MODULE) { | 576 | if (info->flags & FBINFO_MODULE) { |
577 | logo_shown = FBCON_LOGO_DONTSHOW; | 577 | logo_shown = FBCON_LOGO_DONTSHOW; |
578 | return; | 578 | return; |
579 | } | 579 | } |
580 | 580 | ||
581 | /* | 581 | /* |
582 | * remove underline attribute from erase character | 582 | * remove underline attribute from erase character |
583 | * if black and white framebuffer. | 583 | * if black and white framebuffer. |
584 | */ | 584 | */ |
585 | if (fb_get_color_depth(&info->var, &info->fix) == 1) | 585 | if (fb_get_color_depth(&info->var, &info->fix) == 1) |
586 | erase &= ~0x400; | 586 | erase &= ~0x400; |
587 | logo_height = fb_prepare_logo(info, ops->rotate); | 587 | logo_height = fb_prepare_logo(info, ops->rotate); |
588 | logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height); | 588 | logo_lines = DIV_ROUND_UP(logo_height, vc->vc_font.height); |
589 | q = (unsigned short *) (vc->vc_origin + | 589 | q = (unsigned short *) (vc->vc_origin + |
590 | vc->vc_size_row * rows); | 590 | vc->vc_size_row * rows); |
591 | step = logo_lines * cols; | 591 | step = logo_lines * cols; |
592 | for (r = q - logo_lines * cols; r < q; r++) | 592 | for (r = q - logo_lines * cols; r < q; r++) |
593 | if (scr_readw(r) != vc->vc_video_erase_char) | 593 | if (scr_readw(r) != vc->vc_video_erase_char) |
594 | break; | 594 | break; |
595 | if (r != q && new_rows >= rows + logo_lines) { | 595 | if (r != q && new_rows >= rows + logo_lines) { |
596 | save = kmalloc(logo_lines * new_cols * 2, GFP_KERNEL); | 596 | save = kmalloc(logo_lines * new_cols * 2, GFP_KERNEL); |
597 | if (save) { | 597 | if (save) { |
598 | int i = cols < new_cols ? cols : new_cols; | 598 | int i = cols < new_cols ? cols : new_cols; |
599 | scr_memsetw(save, erase, logo_lines * new_cols * 2); | 599 | scr_memsetw(save, erase, logo_lines * new_cols * 2); |
600 | r = q - step; | 600 | r = q - step; |
601 | for (cnt = 0; cnt < logo_lines; cnt++, r += i) | 601 | for (cnt = 0; cnt < logo_lines; cnt++, r += i) |
602 | scr_memcpyw(save + cnt * new_cols, r, 2 * i); | 602 | scr_memcpyw(save + cnt * new_cols, r, 2 * i); |
603 | r = q; | 603 | r = q; |
604 | } | 604 | } |
605 | } | 605 | } |
606 | if (r == q) { | 606 | if (r == q) { |
607 | /* We can scroll screen down */ | 607 | /* We can scroll screen down */ |
608 | r = q - step - cols; | 608 | r = q - step - cols; |
609 | for (cnt = rows - logo_lines; cnt > 0; cnt--) { | 609 | for (cnt = rows - logo_lines; cnt > 0; cnt--) { |
610 | scr_memcpyw(r + step, r, vc->vc_size_row); | 610 | scr_memcpyw(r + step, r, vc->vc_size_row); |
611 | r -= cols; | 611 | r -= cols; |
612 | } | 612 | } |
613 | if (!save) { | 613 | if (!save) { |
614 | int lines; | 614 | int lines; |
615 | if (vc->vc_y + logo_lines >= rows) | 615 | if (vc->vc_y + logo_lines >= rows) |
616 | lines = rows - vc->vc_y - 1; | 616 | lines = rows - vc->vc_y - 1; |
617 | else | 617 | else |
618 | lines = logo_lines; | 618 | lines = logo_lines; |
619 | vc->vc_y += lines; | 619 | vc->vc_y += lines; |
620 | vc->vc_pos += lines * vc->vc_size_row; | 620 | vc->vc_pos += lines * vc->vc_size_row; |
621 | } | 621 | } |
622 | } | 622 | } |
623 | scr_memsetw((unsigned short *) vc->vc_origin, | 623 | scr_memsetw((unsigned short *) vc->vc_origin, |
624 | erase, | 624 | erase, |
625 | vc->vc_size_row * logo_lines); | 625 | vc->vc_size_row * logo_lines); |
626 | 626 | ||
627 | if (CON_IS_VISIBLE(vc) && vc->vc_mode == KD_TEXT) { | 627 | if (CON_IS_VISIBLE(vc) && vc->vc_mode == KD_TEXT) { |
628 | fbcon_clear_margins(vc, 0); | 628 | fbcon_clear_margins(vc, 0); |
629 | update_screen(vc); | 629 | update_screen(vc); |
630 | } | 630 | } |
631 | 631 | ||
632 | if (save) { | 632 | if (save) { |
633 | q = (unsigned short *) (vc->vc_origin + | 633 | q = (unsigned short *) (vc->vc_origin + |
634 | vc->vc_size_row * | 634 | vc->vc_size_row * |
635 | rows); | 635 | rows); |
636 | scr_memcpyw(q, save, logo_lines * new_cols * 2); | 636 | scr_memcpyw(q, save, logo_lines * new_cols * 2); |
637 | vc->vc_y += logo_lines; | 637 | vc->vc_y += logo_lines; |
638 | vc->vc_pos += logo_lines * vc->vc_size_row; | 638 | vc->vc_pos += logo_lines * vc->vc_size_row; |
639 | kfree(save); | 639 | kfree(save); |
640 | } | 640 | } |
641 | 641 | ||
642 | if (logo_lines > vc->vc_bottom) { | 642 | if (logo_lines > vc->vc_bottom) { |
643 | logo_shown = FBCON_LOGO_CANSHOW; | 643 | logo_shown = FBCON_LOGO_CANSHOW; |
644 | printk(KERN_INFO | 644 | printk(KERN_INFO |
645 | "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n"); | 645 | "fbcon_init: disable boot-logo (boot-logo bigger than screen).\n"); |
646 | } else if (logo_shown != FBCON_LOGO_DONTSHOW) { | 646 | } else if (logo_shown != FBCON_LOGO_DONTSHOW) { |
647 | logo_shown = FBCON_LOGO_DRAW; | 647 | logo_shown = FBCON_LOGO_DRAW; |
648 | vc->vc_top = logo_lines; | 648 | vc->vc_top = logo_lines; |
649 | } | 649 | } |
650 | } | 650 | } |
651 | #endif /* MODULE */ | 651 | #endif /* MODULE */ |
652 | 652 | ||
653 | #ifdef CONFIG_FB_TILEBLITTING | 653 | #ifdef CONFIG_FB_TILEBLITTING |
654 | static void set_blitting_type(struct vc_data *vc, struct fb_info *info) | 654 | static void set_blitting_type(struct vc_data *vc, struct fb_info *info) |
655 | { | 655 | { |
656 | struct fbcon_ops *ops = info->fbcon_par; | 656 | struct fbcon_ops *ops = info->fbcon_par; |
657 | 657 | ||
658 | ops->p = &fb_display[vc->vc_num]; | 658 | ops->p = &fb_display[vc->vc_num]; |
659 | 659 | ||
660 | if ((info->flags & FBINFO_MISC_TILEBLITTING)) | 660 | if ((info->flags & FBINFO_MISC_TILEBLITTING)) |
661 | fbcon_set_tileops(vc, info); | 661 | fbcon_set_tileops(vc, info); |
662 | else { | 662 | else { |
663 | fbcon_set_rotation(info); | 663 | fbcon_set_rotation(info); |
664 | fbcon_set_bitops(ops); | 664 | fbcon_set_bitops(ops); |
665 | } | 665 | } |
666 | } | 666 | } |
667 | 667 | ||
668 | static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount) | 668 | static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount) |
669 | { | 669 | { |
670 | int err = 0; | 670 | int err = 0; |
671 | 671 | ||
672 | if (info->flags & FBINFO_MISC_TILEBLITTING && | 672 | if (info->flags & FBINFO_MISC_TILEBLITTING && |
673 | info->tileops->fb_get_tilemax(info) < charcount) | 673 | info->tileops->fb_get_tilemax(info) < charcount) |
674 | err = 1; | 674 | err = 1; |
675 | 675 | ||
676 | return err; | 676 | return err; |
677 | } | 677 | } |
678 | #else | 678 | #else |
679 | static void set_blitting_type(struct vc_data *vc, struct fb_info *info) | 679 | static void set_blitting_type(struct vc_data *vc, struct fb_info *info) |
680 | { | 680 | { |
681 | struct fbcon_ops *ops = info->fbcon_par; | 681 | struct fbcon_ops *ops = info->fbcon_par; |
682 | 682 | ||
683 | info->flags &= ~FBINFO_MISC_TILEBLITTING; | 683 | info->flags &= ~FBINFO_MISC_TILEBLITTING; |
684 | ops->p = &fb_display[vc->vc_num]; | 684 | ops->p = &fb_display[vc->vc_num]; |
685 | fbcon_set_rotation(info); | 685 | fbcon_set_rotation(info); |
686 | fbcon_set_bitops(ops); | 686 | fbcon_set_bitops(ops); |
687 | } | 687 | } |
688 | 688 | ||
689 | static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount) | 689 | static int fbcon_invalid_charcount(struct fb_info *info, unsigned charcount) |
690 | { | 690 | { |
691 | return 0; | 691 | return 0; |
692 | } | 692 | } |
693 | 693 | ||
694 | #endif /* CONFIG_MISC_TILEBLITTING */ | 694 | #endif /* CONFIG_MISC_TILEBLITTING */ |
695 | 695 | ||
696 | 696 | ||
697 | static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info, | 697 | static int con2fb_acquire_newinfo(struct vc_data *vc, struct fb_info *info, |
698 | int unit, int oldidx) | 698 | int unit, int oldidx) |
699 | { | 699 | { |
700 | struct fbcon_ops *ops = NULL; | 700 | struct fbcon_ops *ops = NULL; |
701 | int err = 0; | 701 | int err = 0; |
702 | 702 | ||
703 | if (!try_module_get(info->fbops->owner)) | 703 | if (!try_module_get(info->fbops->owner)) |
704 | err = -ENODEV; | 704 | err = -ENODEV; |
705 | 705 | ||
706 | if (!err && info->fbops->fb_open && | 706 | if (!err && info->fbops->fb_open && |
707 | info->fbops->fb_open(info, 0)) | 707 | info->fbops->fb_open(info, 0)) |
708 | err = -ENODEV; | 708 | err = -ENODEV; |
709 | 709 | ||
710 | if (!err) { | 710 | if (!err) { |
711 | ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL); | 711 | ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL); |
712 | if (!ops) | 712 | if (!ops) |
713 | err = -ENOMEM; | 713 | err = -ENOMEM; |
714 | } | 714 | } |
715 | 715 | ||
716 | if (!err) { | 716 | if (!err) { |
717 | info->fbcon_par = ops; | 717 | info->fbcon_par = ops; |
718 | 718 | ||
719 | if (vc) | 719 | if (vc) |
720 | set_blitting_type(vc, info); | 720 | set_blitting_type(vc, info); |
721 | } | 721 | } |
722 | 722 | ||
723 | if (err) { | 723 | if (err) { |
724 | con2fb_map[unit] = oldidx; | 724 | con2fb_map[unit] = oldidx; |
725 | module_put(info->fbops->owner); | 725 | module_put(info->fbops->owner); |
726 | } | 726 | } |
727 | 727 | ||
728 | return err; | 728 | return err; |
729 | } | 729 | } |
730 | 730 | ||
731 | static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo, | 731 | static int con2fb_release_oldinfo(struct vc_data *vc, struct fb_info *oldinfo, |
732 | struct fb_info *newinfo, int unit, | 732 | struct fb_info *newinfo, int unit, |
733 | int oldidx, int found) | 733 | int oldidx, int found) |
734 | { | 734 | { |
735 | struct fbcon_ops *ops = oldinfo->fbcon_par; | 735 | struct fbcon_ops *ops = oldinfo->fbcon_par; |
736 | int err = 0, ret; | 736 | int err = 0, ret; |
737 | 737 | ||
738 | if (oldinfo->fbops->fb_release && | 738 | if (oldinfo->fbops->fb_release && |
739 | oldinfo->fbops->fb_release(oldinfo, 0)) { | 739 | oldinfo->fbops->fb_release(oldinfo, 0)) { |
740 | con2fb_map[unit] = oldidx; | 740 | con2fb_map[unit] = oldidx; |
741 | if (!found && newinfo->fbops->fb_release) | 741 | if (!found && newinfo->fbops->fb_release) |
742 | newinfo->fbops->fb_release(newinfo, 0); | 742 | newinfo->fbops->fb_release(newinfo, 0); |
743 | if (!found) | 743 | if (!found) |
744 | module_put(newinfo->fbops->owner); | 744 | module_put(newinfo->fbops->owner); |
745 | err = -ENODEV; | 745 | err = -ENODEV; |
746 | } | 746 | } |
747 | 747 | ||
748 | if (!err) { | 748 | if (!err) { |
749 | fbcon_del_cursor_timer(oldinfo); | 749 | fbcon_del_cursor_timer(oldinfo); |
750 | kfree(ops->cursor_state.mask); | 750 | kfree(ops->cursor_state.mask); |
751 | kfree(ops->cursor_data); | 751 | kfree(ops->cursor_data); |
752 | kfree(ops->fontbuffer); | 752 | kfree(ops->fontbuffer); |
753 | kfree(oldinfo->fbcon_par); | 753 | kfree(oldinfo->fbcon_par); |
754 | oldinfo->fbcon_par = NULL; | 754 | oldinfo->fbcon_par = NULL; |
755 | module_put(oldinfo->fbops->owner); | 755 | module_put(oldinfo->fbops->owner); |
756 | /* | 756 | /* |
757 | If oldinfo and newinfo are driving the same hardware, | 757 | If oldinfo and newinfo are driving the same hardware, |
758 | the fb_release() method of oldinfo may attempt to | 758 | the fb_release() method of oldinfo may attempt to |
759 | restore the hardware state. This will leave the | 759 | restore the hardware state. This will leave the |
760 | newinfo in an undefined state. Thus, a call to | 760 | newinfo in an undefined state. Thus, a call to |
761 | fb_set_par() may be needed for the newinfo. | 761 | fb_set_par() may be needed for the newinfo. |
762 | */ | 762 | */ |
763 | if (newinfo->fbops->fb_set_par) { | 763 | if (newinfo->fbops->fb_set_par) { |
764 | ret = newinfo->fbops->fb_set_par(newinfo); | 764 | ret = newinfo->fbops->fb_set_par(newinfo); |
765 | 765 | ||
766 | if (ret) | 766 | if (ret) |
767 | printk(KERN_ERR "con2fb_release_oldinfo: " | 767 | printk(KERN_ERR "con2fb_release_oldinfo: " |
768 | "detected unhandled fb_set_par error, " | 768 | "detected unhandled fb_set_par error, " |
769 | "error code %d\n", ret); | 769 | "error code %d\n", ret); |
770 | } | 770 | } |
771 | } | 771 | } |
772 | 772 | ||
773 | return err; | 773 | return err; |
774 | } | 774 | } |
775 | 775 | ||
776 | static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, | 776 | static void con2fb_init_display(struct vc_data *vc, struct fb_info *info, |
777 | int unit, int show_logo) | 777 | int unit, int show_logo) |
778 | { | 778 | { |
779 | struct fbcon_ops *ops = info->fbcon_par; | 779 | struct fbcon_ops *ops = info->fbcon_par; |
780 | int ret; | 780 | int ret; |
781 | 781 | ||
782 | ops->currcon = fg_console; | 782 | ops->currcon = fg_console; |
783 | 783 | ||
784 | if (info->fbops->fb_set_par && !(ops->flags & FBCON_FLAGS_INIT)) { | 784 | if (info->fbops->fb_set_par && !(ops->flags & FBCON_FLAGS_INIT)) { |
785 | ret = info->fbops->fb_set_par(info); | 785 | ret = info->fbops->fb_set_par(info); |
786 | 786 | ||
787 | if (ret) | 787 | if (ret) |
788 | printk(KERN_ERR "con2fb_init_display: detected " | 788 | printk(KERN_ERR "con2fb_init_display: detected " |
789 | "unhandled fb_set_par error, " | 789 | "unhandled fb_set_par error, " |
790 | "error code %d\n", ret); | 790 | "error code %d\n", ret); |
791 | } | 791 | } |
792 | 792 | ||
793 | ops->flags |= FBCON_FLAGS_INIT; | 793 | ops->flags |= FBCON_FLAGS_INIT; |
794 | ops->graphics = 0; | 794 | ops->graphics = 0; |
795 | fbcon_set_disp(info, &info->var, unit); | 795 | fbcon_set_disp(info, &info->var, unit); |
796 | 796 | ||
797 | if (show_logo) { | 797 | if (show_logo) { |
798 | struct vc_data *fg_vc = vc_cons[fg_console].d; | 798 | struct vc_data *fg_vc = vc_cons[fg_console].d; |
799 | struct fb_info *fg_info = | 799 | struct fb_info *fg_info = |
800 | registered_fb[con2fb_map[fg_console]]; | 800 | registered_fb[con2fb_map[fg_console]]; |
801 | 801 | ||
802 | fbcon_prepare_logo(fg_vc, fg_info, fg_vc->vc_cols, | 802 | fbcon_prepare_logo(fg_vc, fg_info, fg_vc->vc_cols, |
803 | fg_vc->vc_rows, fg_vc->vc_cols, | 803 | fg_vc->vc_rows, fg_vc->vc_cols, |
804 | fg_vc->vc_rows); | 804 | fg_vc->vc_rows); |
805 | } | 805 | } |
806 | 806 | ||
807 | update_screen(vc_cons[fg_console].d); | 807 | update_screen(vc_cons[fg_console].d); |
808 | } | 808 | } |
809 | 809 | ||
810 | /** | 810 | /** |
811 | * set_con2fb_map - map console to frame buffer device | 811 | * set_con2fb_map - map console to frame buffer device |
812 | * @unit: virtual console number to map | 812 | * @unit: virtual console number to map |
813 | * @newidx: frame buffer index to map virtual console to | 813 | * @newidx: frame buffer index to map virtual console to |
814 | * @user: user request | 814 | * @user: user request |
815 | * | 815 | * |
816 | * Maps a virtual console @unit to a frame buffer device | 816 | * Maps a virtual console @unit to a frame buffer device |
817 | * @newidx. | 817 | * @newidx. |
818 | */ | 818 | */ |
819 | static int set_con2fb_map(int unit, int newidx, int user) | 819 | static int set_con2fb_map(int unit, int newidx, int user) |
820 | { | 820 | { |
821 | struct vc_data *vc = vc_cons[unit].d; | 821 | struct vc_data *vc = vc_cons[unit].d; |
822 | int oldidx = con2fb_map[unit]; | 822 | int oldidx = con2fb_map[unit]; |
823 | struct fb_info *info = registered_fb[newidx]; | 823 | struct fb_info *info = registered_fb[newidx]; |
824 | struct fb_info *oldinfo = NULL; | 824 | struct fb_info *oldinfo = NULL; |
825 | int found, err = 0; | 825 | int found, err = 0; |
826 | 826 | ||
827 | if (oldidx == newidx) | 827 | if (oldidx == newidx) |
828 | return 0; | 828 | return 0; |
829 | 829 | ||
830 | if (!info) | 830 | if (!info) |
831 | return -EINVAL; | 831 | return -EINVAL; |
832 | 832 | ||
833 | if (!search_for_mapped_con() || !con_is_bound(&fb_con)) { | 833 | if (!search_for_mapped_con() || !con_is_bound(&fb_con)) { |
834 | info_idx = newidx; | 834 | info_idx = newidx; |
835 | return fbcon_takeover(0); | 835 | return fbcon_takeover(0); |
836 | } | 836 | } |
837 | 837 | ||
838 | if (oldidx != -1) | 838 | if (oldidx != -1) |
839 | oldinfo = registered_fb[oldidx]; | 839 | oldinfo = registered_fb[oldidx]; |
840 | 840 | ||
841 | found = search_fb_in_map(newidx); | 841 | found = search_fb_in_map(newidx); |
842 | 842 | ||
843 | console_lock(); | 843 | console_lock(); |
844 | con2fb_map[unit] = newidx; | 844 | con2fb_map[unit] = newidx; |
845 | if (!err && !found) | 845 | if (!err && !found) |
846 | err = con2fb_acquire_newinfo(vc, info, unit, oldidx); | 846 | err = con2fb_acquire_newinfo(vc, info, unit, oldidx); |
847 | 847 | ||
848 | 848 | ||
849 | /* | 849 | /* |
850 | * If old fb is not mapped to any of the consoles, | 850 | * If old fb is not mapped to any of the consoles, |
851 | * fbcon should release it. | 851 | * fbcon should release it. |
852 | */ | 852 | */ |
853 | if (!err && oldinfo && !search_fb_in_map(oldidx)) | 853 | if (!err && oldinfo && !search_fb_in_map(oldidx)) |
854 | err = con2fb_release_oldinfo(vc, oldinfo, info, unit, oldidx, | 854 | err = con2fb_release_oldinfo(vc, oldinfo, info, unit, oldidx, |
855 | found); | 855 | found); |
856 | 856 | ||
857 | if (!err) { | 857 | if (!err) { |
858 | int show_logo = (fg_console == 0 && !user && | 858 | int show_logo = (fg_console == 0 && !user && |
859 | logo_shown != FBCON_LOGO_DONTSHOW); | 859 | logo_shown != FBCON_LOGO_DONTSHOW); |
860 | 860 | ||
861 | if (!found) | 861 | if (!found) |
862 | fbcon_add_cursor_timer(info); | 862 | fbcon_add_cursor_timer(info); |
863 | con2fb_map_boot[unit] = newidx; | 863 | con2fb_map_boot[unit] = newidx; |
864 | con2fb_init_display(vc, info, unit, show_logo); | 864 | con2fb_init_display(vc, info, unit, show_logo); |
865 | } | 865 | } |
866 | 866 | ||
867 | if (!search_fb_in_map(info_idx)) | 867 | if (!search_fb_in_map(info_idx)) |
868 | info_idx = newidx; | 868 | info_idx = newidx; |
869 | 869 | ||
870 | console_unlock(); | 870 | console_unlock(); |
871 | return err; | 871 | return err; |
872 | } | 872 | } |
873 | 873 | ||
874 | /* | 874 | /* |
875 | * Low Level Operations | 875 | * Low Level Operations |
876 | */ | 876 | */ |
877 | /* NOTE: fbcon cannot be __init: it may be called from take_over_console later */ | 877 | /* NOTE: fbcon cannot be __init: it may be called from take_over_console later */ |
878 | static int var_to_display(struct display *disp, | 878 | static int var_to_display(struct display *disp, |
879 | struct fb_var_screeninfo *var, | 879 | struct fb_var_screeninfo *var, |
880 | struct fb_info *info) | 880 | struct fb_info *info) |
881 | { | 881 | { |
882 | disp->xres_virtual = var->xres_virtual; | 882 | disp->xres_virtual = var->xres_virtual; |
883 | disp->yres_virtual = var->yres_virtual; | 883 | disp->yres_virtual = var->yres_virtual; |
884 | disp->bits_per_pixel = var->bits_per_pixel; | 884 | disp->bits_per_pixel = var->bits_per_pixel; |
885 | disp->grayscale = var->grayscale; | 885 | disp->grayscale = var->grayscale; |
886 | disp->nonstd = var->nonstd; | 886 | disp->nonstd = var->nonstd; |
887 | disp->accel_flags = var->accel_flags; | 887 | disp->accel_flags = var->accel_flags; |
888 | disp->height = var->height; | 888 | disp->height = var->height; |
889 | disp->width = var->width; | 889 | disp->width = var->width; |
890 | disp->red = var->red; | 890 | disp->red = var->red; |
891 | disp->green = var->green; | 891 | disp->green = var->green; |
892 | disp->blue = var->blue; | 892 | disp->blue = var->blue; |
893 | disp->transp = var->transp; | 893 | disp->transp = var->transp; |
894 | disp->rotate = var->rotate; | 894 | disp->rotate = var->rotate; |
895 | disp->mode = fb_match_mode(var, &info->modelist); | 895 | disp->mode = fb_match_mode(var, &info->modelist); |
896 | if (disp->mode == NULL) | 896 | if (disp->mode == NULL) |
897 | /* This should not happen */ | 897 | /* This should not happen */ |
898 | return -EINVAL; | 898 | return -EINVAL; |
899 | return 0; | 899 | return 0; |
900 | } | 900 | } |
901 | 901 | ||
902 | static void display_to_var(struct fb_var_screeninfo *var, | 902 | static void display_to_var(struct fb_var_screeninfo *var, |
903 | struct display *disp) | 903 | struct display *disp) |
904 | { | 904 | { |
905 | fb_videomode_to_var(var, disp->mode); | 905 | fb_videomode_to_var(var, disp->mode); |
906 | var->xres_virtual = disp->xres_virtual; | 906 | var->xres_virtual = disp->xres_virtual; |
907 | var->yres_virtual = disp->yres_virtual; | 907 | var->yres_virtual = disp->yres_virtual; |
908 | var->bits_per_pixel = disp->bits_per_pixel; | 908 | var->bits_per_pixel = disp->bits_per_pixel; |
909 | var->grayscale = disp->grayscale; | 909 | var->grayscale = disp->grayscale; |
910 | var->nonstd = disp->nonstd; | 910 | var->nonstd = disp->nonstd; |
911 | var->accel_flags = disp->accel_flags; | 911 | var->accel_flags = disp->accel_flags; |
912 | var->height = disp->height; | 912 | var->height = disp->height; |
913 | var->width = disp->width; | 913 | var->width = disp->width; |
914 | var->red = disp->red; | 914 | var->red = disp->red; |
915 | var->green = disp->green; | 915 | var->green = disp->green; |
916 | var->blue = disp->blue; | 916 | var->blue = disp->blue; |
917 | var->transp = disp->transp; | 917 | var->transp = disp->transp; |
918 | var->rotate = disp->rotate; | 918 | var->rotate = disp->rotate; |
919 | } | 919 | } |
920 | 920 | ||
921 | static const char *fbcon_startup(void) | 921 | static const char *fbcon_startup(void) |
922 | { | 922 | { |
923 | const char *display_desc = "frame buffer device"; | 923 | const char *display_desc = "frame buffer device"; |
924 | struct display *p = &fb_display[fg_console]; | 924 | struct display *p = &fb_display[fg_console]; |
925 | struct vc_data *vc = vc_cons[fg_console].d; | 925 | struct vc_data *vc = vc_cons[fg_console].d; |
926 | const struct font_desc *font = NULL; | 926 | const struct font_desc *font = NULL; |
927 | struct module *owner; | 927 | struct module *owner; |
928 | struct fb_info *info = NULL; | 928 | struct fb_info *info = NULL; |
929 | struct fbcon_ops *ops; | 929 | struct fbcon_ops *ops; |
930 | int rows, cols; | 930 | int rows, cols; |
931 | 931 | ||
932 | /* | 932 | /* |
933 | * If num_registered_fb is zero, this is a call for the dummy part. | 933 | * If num_registered_fb is zero, this is a call for the dummy part. |
934 | * The frame buffer devices weren't initialized yet. | 934 | * The frame buffer devices weren't initialized yet. |
935 | */ | 935 | */ |
936 | if (!num_registered_fb || info_idx == -1) | 936 | if (!num_registered_fb || info_idx == -1) |
937 | return display_desc; | 937 | return display_desc; |
938 | /* | 938 | /* |
939 | * Instead of blindly using registered_fb[0], we use info_idx, set by | 939 | * Instead of blindly using registered_fb[0], we use info_idx, set by |
940 | * fb_console_init(); | 940 | * fb_console_init(); |
941 | */ | 941 | */ |
942 | info = registered_fb[info_idx]; | 942 | info = registered_fb[info_idx]; |
943 | if (!info) | 943 | if (!info) |
944 | return NULL; | 944 | return NULL; |
945 | 945 | ||
946 | owner = info->fbops->owner; | 946 | owner = info->fbops->owner; |
947 | if (!try_module_get(owner)) | 947 | if (!try_module_get(owner)) |
948 | return NULL; | 948 | return NULL; |
949 | if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) { | 949 | if (info->fbops->fb_open && info->fbops->fb_open(info, 0)) { |
950 | module_put(owner); | 950 | module_put(owner); |
951 | return NULL; | 951 | return NULL; |
952 | } | 952 | } |
953 | 953 | ||
954 | ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL); | 954 | ops = kzalloc(sizeof(struct fbcon_ops), GFP_KERNEL); |
955 | if (!ops) { | 955 | if (!ops) { |
956 | module_put(owner); | 956 | module_put(owner); |
957 | return NULL; | 957 | return NULL; |
958 | } | 958 | } |
959 | 959 | ||
960 | ops->currcon = -1; | 960 | ops->currcon = -1; |
961 | ops->graphics = 1; | 961 | ops->graphics = 1; |
962 | ops->cur_rotate = -1; | 962 | ops->cur_rotate = -1; |
963 | info->fbcon_par = ops; | 963 | info->fbcon_par = ops; |
964 | p->con_rotate = initial_rotation; | 964 | p->con_rotate = initial_rotation; |
965 | set_blitting_type(vc, info); | 965 | set_blitting_type(vc, info); |
966 | 966 | ||
967 | if (info->fix.type != FB_TYPE_TEXT) { | 967 | if (info->fix.type != FB_TYPE_TEXT) { |
968 | if (fbcon_softback_size) { | 968 | if (fbcon_softback_size) { |
969 | if (!softback_buf) { | 969 | if (!softback_buf) { |
970 | softback_buf = | 970 | softback_buf = |
971 | (unsigned long) | 971 | (unsigned long) |
972 | kmalloc(fbcon_softback_size, | 972 | kmalloc(fbcon_softback_size, |
973 | GFP_KERNEL); | 973 | GFP_KERNEL); |
974 | if (!softback_buf) { | 974 | if (!softback_buf) { |
975 | fbcon_softback_size = 0; | 975 | fbcon_softback_size = 0; |
976 | softback_top = 0; | 976 | softback_top = 0; |
977 | } | 977 | } |
978 | } | 978 | } |
979 | } else { | 979 | } else { |
980 | if (softback_buf) { | 980 | if (softback_buf) { |
981 | kfree((void *) softback_buf); | 981 | kfree((void *) softback_buf); |
982 | softback_buf = 0; | 982 | softback_buf = 0; |
983 | softback_top = 0; | 983 | softback_top = 0; |
984 | } | 984 | } |
985 | } | 985 | } |
986 | if (softback_buf) | 986 | if (softback_buf) |
987 | softback_in = softback_top = softback_curr = | 987 | softback_in = softback_top = softback_curr = |
988 | softback_buf; | 988 | softback_buf; |
989 | softback_lines = 0; | 989 | softback_lines = 0; |
990 | } | 990 | } |
991 | 991 | ||
992 | /* Setup default font */ | 992 | /* Setup default font */ |
993 | if (!p->fontdata) { | 993 | if (!p->fontdata) { |
994 | if (!fontname[0] || !(font = find_font(fontname))) | 994 | if (!fontname[0] || !(font = find_font(fontname))) |
995 | font = get_default_font(info->var.xres, | 995 | font = get_default_font(info->var.xres, |
996 | info->var.yres, | 996 | info->var.yres, |
997 | info->pixmap.blit_x, | 997 | info->pixmap.blit_x, |
998 | info->pixmap.blit_y); | 998 | info->pixmap.blit_y); |
999 | vc->vc_font.width = font->width; | 999 | vc->vc_font.width = font->width; |
1000 | vc->vc_font.height = font->height; | 1000 | vc->vc_font.height = font->height; |
1001 | vc->vc_font.data = (void *)(p->fontdata = font->data); | 1001 | vc->vc_font.data = (void *)(p->fontdata = font->data); |
1002 | vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */ | 1002 | vc->vc_font.charcount = 256; /* FIXME Need to support more fonts */ |
1003 | } | 1003 | } |
1004 | 1004 | ||
1005 | cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); | 1005 | cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); |
1006 | rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); | 1006 | rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); |
1007 | cols /= vc->vc_font.width; | 1007 | cols /= vc->vc_font.width; |
1008 | rows /= vc->vc_font.height; | 1008 | rows /= vc->vc_font.height; |
1009 | vc_resize(vc, cols, rows); | 1009 | vc_resize(vc, cols, rows); |
1010 | 1010 | ||
1011 | DPRINTK("mode: %s\n", info->fix.id); | 1011 | DPRINTK("mode: %s\n", info->fix.id); |
1012 | DPRINTK("visual: %d\n", info->fix.visual); | 1012 | DPRINTK("visual: %d\n", info->fix.visual); |
1013 | DPRINTK("res: %dx%d-%d\n", info->var.xres, | 1013 | DPRINTK("res: %dx%d-%d\n", info->var.xres, |
1014 | info->var.yres, | 1014 | info->var.yres, |
1015 | info->var.bits_per_pixel); | 1015 | info->var.bits_per_pixel); |
1016 | 1016 | ||
1017 | fbcon_add_cursor_timer(info); | 1017 | fbcon_add_cursor_timer(info); |
1018 | fbcon_has_exited = 0; | 1018 | fbcon_has_exited = 0; |
1019 | return display_desc; | 1019 | return display_desc; |
1020 | } | 1020 | } |
1021 | 1021 | ||
1022 | static void fbcon_init(struct vc_data *vc, int init) | 1022 | static void fbcon_init(struct vc_data *vc, int init) |
1023 | { | 1023 | { |
1024 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 1024 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
1025 | struct fbcon_ops *ops; | 1025 | struct fbcon_ops *ops; |
1026 | struct vc_data **default_mode = vc->vc_display_fg; | 1026 | struct vc_data **default_mode = vc->vc_display_fg; |
1027 | struct vc_data *svc = *default_mode; | 1027 | struct vc_data *svc = *default_mode; |
1028 | struct display *t, *p = &fb_display[vc->vc_num]; | 1028 | struct display *t, *p = &fb_display[vc->vc_num]; |
1029 | int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256; | 1029 | int logo = 1, new_rows, new_cols, rows, cols, charcnt = 256; |
1030 | int cap, ret; | 1030 | int cap, ret; |
1031 | 1031 | ||
1032 | if (info_idx == -1 || info == NULL) | 1032 | if (info_idx == -1 || info == NULL) |
1033 | return; | 1033 | return; |
1034 | 1034 | ||
1035 | cap = info->flags; | 1035 | cap = info->flags; |
1036 | 1036 | ||
1037 | if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW || | 1037 | if (vc != svc || logo_shown == FBCON_LOGO_DONTSHOW || |
1038 | (info->fix.type == FB_TYPE_TEXT)) | 1038 | (info->fix.type == FB_TYPE_TEXT)) |
1039 | logo = 0; | 1039 | logo = 0; |
1040 | 1040 | ||
1041 | if (var_to_display(p, &info->var, info)) | 1041 | if (var_to_display(p, &info->var, info)) |
1042 | return; | 1042 | return; |
1043 | 1043 | ||
1044 | if (!info->fbcon_par) | 1044 | if (!info->fbcon_par) |
1045 | con2fb_acquire_newinfo(vc, info, vc->vc_num, -1); | 1045 | con2fb_acquire_newinfo(vc, info, vc->vc_num, -1); |
1046 | 1046 | ||
1047 | /* If we are not the first console on this | 1047 | /* If we are not the first console on this |
1048 | fb, copy the font from that console */ | 1048 | fb, copy the font from that console */ |
1049 | t = &fb_display[fg_console]; | 1049 | t = &fb_display[fg_console]; |
1050 | if (!p->fontdata) { | 1050 | if (!p->fontdata) { |
1051 | if (t->fontdata) { | 1051 | if (t->fontdata) { |
1052 | struct vc_data *fvc = vc_cons[fg_console].d; | 1052 | struct vc_data *fvc = vc_cons[fg_console].d; |
1053 | 1053 | ||
1054 | vc->vc_font.data = (void *)(p->fontdata = | 1054 | vc->vc_font.data = (void *)(p->fontdata = |
1055 | fvc->vc_font.data); | 1055 | fvc->vc_font.data); |
1056 | vc->vc_font.width = fvc->vc_font.width; | 1056 | vc->vc_font.width = fvc->vc_font.width; |
1057 | vc->vc_font.height = fvc->vc_font.height; | 1057 | vc->vc_font.height = fvc->vc_font.height; |
1058 | p->userfont = t->userfont; | 1058 | p->userfont = t->userfont; |
1059 | 1059 | ||
1060 | if (p->userfont) | 1060 | if (p->userfont) |
1061 | REFCOUNT(p->fontdata)++; | 1061 | REFCOUNT(p->fontdata)++; |
1062 | } else { | 1062 | } else { |
1063 | const struct font_desc *font = NULL; | 1063 | const struct font_desc *font = NULL; |
1064 | 1064 | ||
1065 | if (!fontname[0] || !(font = find_font(fontname))) | 1065 | if (!fontname[0] || !(font = find_font(fontname))) |
1066 | font = get_default_font(info->var.xres, | 1066 | font = get_default_font(info->var.xres, |
1067 | info->var.yres, | 1067 | info->var.yres, |
1068 | info->pixmap.blit_x, | 1068 | info->pixmap.blit_x, |
1069 | info->pixmap.blit_y); | 1069 | info->pixmap.blit_y); |
1070 | vc->vc_font.width = font->width; | 1070 | vc->vc_font.width = font->width; |
1071 | vc->vc_font.height = font->height; | 1071 | vc->vc_font.height = font->height; |
1072 | vc->vc_font.data = (void *)(p->fontdata = font->data); | 1072 | vc->vc_font.data = (void *)(p->fontdata = font->data); |
1073 | vc->vc_font.charcount = 256; /* FIXME Need to | 1073 | vc->vc_font.charcount = 256; /* FIXME Need to |
1074 | support more fonts */ | 1074 | support more fonts */ |
1075 | } | 1075 | } |
1076 | } | 1076 | } |
1077 | 1077 | ||
1078 | if (p->userfont) | 1078 | if (p->userfont) |
1079 | charcnt = FNTCHARCNT(p->fontdata); | 1079 | charcnt = FNTCHARCNT(p->fontdata); |
1080 | 1080 | ||
1081 | vc->vc_panic_force_write = !!(info->flags & FBINFO_CAN_FORCE_OUTPUT); | 1081 | vc->vc_panic_force_write = !!(info->flags & FBINFO_CAN_FORCE_OUTPUT); |
1082 | vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); | 1082 | vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); |
1083 | vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; | 1083 | vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; |
1084 | if (charcnt == 256) { | 1084 | if (charcnt == 256) { |
1085 | vc->vc_hi_font_mask = 0; | 1085 | vc->vc_hi_font_mask = 0; |
1086 | } else { | 1086 | } else { |
1087 | vc->vc_hi_font_mask = 0x100; | 1087 | vc->vc_hi_font_mask = 0x100; |
1088 | if (vc->vc_can_do_color) | 1088 | if (vc->vc_can_do_color) |
1089 | vc->vc_complement_mask <<= 1; | 1089 | vc->vc_complement_mask <<= 1; |
1090 | } | 1090 | } |
1091 | 1091 | ||
1092 | if (!*svc->vc_uni_pagedir_loc) | 1092 | if (!*svc->vc_uni_pagedir_loc) |
1093 | con_set_default_unimap(svc); | 1093 | con_set_default_unimap(svc); |
1094 | if (!*vc->vc_uni_pagedir_loc) | 1094 | if (!*vc->vc_uni_pagedir_loc) |
1095 | con_copy_unimap(vc, svc); | 1095 | con_copy_unimap(vc, svc); |
1096 | 1096 | ||
1097 | ops = info->fbcon_par; | 1097 | ops = info->fbcon_par; |
1098 | p->con_rotate = initial_rotation; | 1098 | p->con_rotate = initial_rotation; |
1099 | set_blitting_type(vc, info); | 1099 | set_blitting_type(vc, info); |
1100 | 1100 | ||
1101 | cols = vc->vc_cols; | 1101 | cols = vc->vc_cols; |
1102 | rows = vc->vc_rows; | 1102 | rows = vc->vc_rows; |
1103 | new_cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); | 1103 | new_cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); |
1104 | new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); | 1104 | new_rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); |
1105 | new_cols /= vc->vc_font.width; | 1105 | new_cols /= vc->vc_font.width; |
1106 | new_rows /= vc->vc_font.height; | 1106 | new_rows /= vc->vc_font.height; |
1107 | 1107 | ||
1108 | /* | 1108 | /* |
1109 | * We must always set the mode. The mode of the previous console | 1109 | * We must always set the mode. The mode of the previous console |
1110 | * driver could be in the same resolution but we are using different | 1110 | * driver could be in the same resolution but we are using different |
1111 | * hardware so we have to initialize the hardware. | 1111 | * hardware so we have to initialize the hardware. |
1112 | * | 1112 | * |
1113 | * We need to do it in fbcon_init() to prevent screen corruption. | 1113 | * We need to do it in fbcon_init() to prevent screen corruption. |
1114 | */ | 1114 | */ |
1115 | if (CON_IS_VISIBLE(vc) && vc->vc_mode == KD_TEXT) { | 1115 | if (CON_IS_VISIBLE(vc) && vc->vc_mode == KD_TEXT) { |
1116 | if (info->fbops->fb_set_par && | 1116 | if (info->fbops->fb_set_par && |
1117 | !(ops->flags & FBCON_FLAGS_INIT)) { | 1117 | !(ops->flags & FBCON_FLAGS_INIT)) { |
1118 | ret = info->fbops->fb_set_par(info); | 1118 | ret = info->fbops->fb_set_par(info); |
1119 | 1119 | ||
1120 | if (ret) | 1120 | if (ret) |
1121 | printk(KERN_ERR "fbcon_init: detected " | 1121 | printk(KERN_ERR "fbcon_init: detected " |
1122 | "unhandled fb_set_par error, " | 1122 | "unhandled fb_set_par error, " |
1123 | "error code %d\n", ret); | 1123 | "error code %d\n", ret); |
1124 | } | 1124 | } |
1125 | 1125 | ||
1126 | ops->flags |= FBCON_FLAGS_INIT; | 1126 | ops->flags |= FBCON_FLAGS_INIT; |
1127 | } | 1127 | } |
1128 | 1128 | ||
1129 | ops->graphics = 0; | 1129 | ops->graphics = 0; |
1130 | 1130 | ||
1131 | if ((cap & FBINFO_HWACCEL_COPYAREA) && | 1131 | if ((cap & FBINFO_HWACCEL_COPYAREA) && |
1132 | !(cap & FBINFO_HWACCEL_DISABLED)) | 1132 | !(cap & FBINFO_HWACCEL_DISABLED)) |
1133 | p->scrollmode = SCROLL_MOVE; | 1133 | p->scrollmode = SCROLL_MOVE; |
1134 | else /* default to something safe */ | 1134 | else /* default to something safe */ |
1135 | p->scrollmode = SCROLL_REDRAW; | 1135 | p->scrollmode = SCROLL_REDRAW; |
1136 | 1136 | ||
1137 | /* | 1137 | /* |
1138 | * ++guenther: console.c:vc_allocate() relies on initializing | 1138 | * ++guenther: console.c:vc_allocate() relies on initializing |
1139 | * vc_{cols,rows}, but we must not set those if we are only | 1139 | * vc_{cols,rows}, but we must not set those if we are only |
1140 | * resizing the console. | 1140 | * resizing the console. |
1141 | */ | 1141 | */ |
1142 | if (init) { | 1142 | if (init) { |
1143 | vc->vc_cols = new_cols; | 1143 | vc->vc_cols = new_cols; |
1144 | vc->vc_rows = new_rows; | 1144 | vc->vc_rows = new_rows; |
1145 | } else | 1145 | } else |
1146 | vc_resize(vc, new_cols, new_rows); | 1146 | vc_resize(vc, new_cols, new_rows); |
1147 | 1147 | ||
1148 | if (logo) | 1148 | if (logo) |
1149 | fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows); | 1149 | fbcon_prepare_logo(vc, info, cols, rows, new_cols, new_rows); |
1150 | 1150 | ||
1151 | if (vc == svc && softback_buf) | 1151 | if (vc == svc && softback_buf) |
1152 | fbcon_update_softback(vc); | 1152 | fbcon_update_softback(vc); |
1153 | 1153 | ||
1154 | if (ops->rotate_font && ops->rotate_font(info, vc)) { | 1154 | if (ops->rotate_font && ops->rotate_font(info, vc)) { |
1155 | ops->rotate = FB_ROTATE_UR; | 1155 | ops->rotate = FB_ROTATE_UR; |
1156 | set_blitting_type(vc, info); | 1156 | set_blitting_type(vc, info); |
1157 | } | 1157 | } |
1158 | 1158 | ||
1159 | ops->p = &fb_display[fg_console]; | 1159 | ops->p = &fb_display[fg_console]; |
1160 | } | 1160 | } |
1161 | 1161 | ||
1162 | static void fbcon_free_font(struct display *p) | 1162 | static void fbcon_free_font(struct display *p) |
1163 | { | 1163 | { |
1164 | if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0)) | 1164 | if (p->userfont && p->fontdata && (--REFCOUNT(p->fontdata) == 0)) |
1165 | kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int)); | 1165 | kfree(p->fontdata - FONT_EXTRA_WORDS * sizeof(int)); |
1166 | p->fontdata = NULL; | 1166 | p->fontdata = NULL; |
1167 | p->userfont = 0; | 1167 | p->userfont = 0; |
1168 | } | 1168 | } |
1169 | 1169 | ||
1170 | static void fbcon_deinit(struct vc_data *vc) | 1170 | static void fbcon_deinit(struct vc_data *vc) |
1171 | { | 1171 | { |
1172 | struct display *p = &fb_display[vc->vc_num]; | 1172 | struct display *p = &fb_display[vc->vc_num]; |
1173 | struct fb_info *info; | 1173 | struct fb_info *info; |
1174 | struct fbcon_ops *ops; | 1174 | struct fbcon_ops *ops; |
1175 | int idx; | 1175 | int idx; |
1176 | 1176 | ||
1177 | fbcon_free_font(p); | 1177 | fbcon_free_font(p); |
1178 | idx = con2fb_map[vc->vc_num]; | 1178 | idx = con2fb_map[vc->vc_num]; |
1179 | 1179 | ||
1180 | if (idx == -1) | 1180 | if (idx == -1) |
1181 | goto finished; | 1181 | goto finished; |
1182 | 1182 | ||
1183 | info = registered_fb[idx]; | 1183 | info = registered_fb[idx]; |
1184 | 1184 | ||
1185 | if (!info) | 1185 | if (!info) |
1186 | goto finished; | 1186 | goto finished; |
1187 | 1187 | ||
1188 | ops = info->fbcon_par; | 1188 | ops = info->fbcon_par; |
1189 | 1189 | ||
1190 | if (!ops) | 1190 | if (!ops) |
1191 | goto finished; | 1191 | goto finished; |
1192 | 1192 | ||
1193 | if (CON_IS_VISIBLE(vc)) | 1193 | if (CON_IS_VISIBLE(vc)) |
1194 | fbcon_del_cursor_timer(info); | 1194 | fbcon_del_cursor_timer(info); |
1195 | 1195 | ||
1196 | ops->flags &= ~FBCON_FLAGS_INIT; | 1196 | ops->flags &= ~FBCON_FLAGS_INIT; |
1197 | finished: | 1197 | finished: |
1198 | 1198 | ||
1199 | if (!con_is_bound(&fb_con)) | 1199 | if (!con_is_bound(&fb_con)) |
1200 | fbcon_exit(); | 1200 | fbcon_exit(); |
1201 | 1201 | ||
1202 | return; | 1202 | return; |
1203 | } | 1203 | } |
1204 | 1204 | ||
1205 | /* ====================================================================== */ | 1205 | /* ====================================================================== */ |
1206 | 1206 | ||
1207 | /* fbcon_XXX routines - interface used by the world | 1207 | /* fbcon_XXX routines - interface used by the world |
1208 | * | 1208 | * |
1209 | * This system is now divided into two levels because of complications | 1209 | * This system is now divided into two levels because of complications |
1210 | * caused by hardware scrolling. Top level functions: | 1210 | * caused by hardware scrolling. Top level functions: |
1211 | * | 1211 | * |
1212 | * fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins() | 1212 | * fbcon_bmove(), fbcon_clear(), fbcon_putc(), fbcon_clear_margins() |
1213 | * | 1213 | * |
1214 | * handles y values in range [0, scr_height-1] that correspond to real | 1214 | * handles y values in range [0, scr_height-1] that correspond to real |
1215 | * screen positions. y_wrap shift means that first line of bitmap may be | 1215 | * screen positions. y_wrap shift means that first line of bitmap may be |
1216 | * anywhere on this display. These functions convert lineoffsets to | 1216 | * anywhere on this display. These functions convert lineoffsets to |
1217 | * bitmap offsets and deal with the wrap-around case by splitting blits. | 1217 | * bitmap offsets and deal with the wrap-around case by splitting blits. |
1218 | * | 1218 | * |
1219 | * fbcon_bmove_physical_8() -- These functions fast implementations | 1219 | * fbcon_bmove_physical_8() -- These functions fast implementations |
1220 | * fbcon_clear_physical_8() -- of original fbcon_XXX fns. | 1220 | * fbcon_clear_physical_8() -- of original fbcon_XXX fns. |
1221 | * fbcon_putc_physical_8() -- (font width != 8) may be added later | 1221 | * fbcon_putc_physical_8() -- (font width != 8) may be added later |
1222 | * | 1222 | * |
1223 | * WARNING: | 1223 | * WARNING: |
1224 | * | 1224 | * |
1225 | * At the moment fbcon_putc() cannot blit across vertical wrap boundary | 1225 | * At the moment fbcon_putc() cannot blit across vertical wrap boundary |
1226 | * Implies should only really hardware scroll in rows. Only reason for | 1226 | * Implies should only really hardware scroll in rows. Only reason for |
1227 | * restriction is simplicity & efficiency at the moment. | 1227 | * restriction is simplicity & efficiency at the moment. |
1228 | */ | 1228 | */ |
1229 | 1229 | ||
1230 | static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, | 1230 | static void fbcon_clear(struct vc_data *vc, int sy, int sx, int height, |
1231 | int width) | 1231 | int width) |
1232 | { | 1232 | { |
1233 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 1233 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
1234 | struct fbcon_ops *ops = info->fbcon_par; | 1234 | struct fbcon_ops *ops = info->fbcon_par; |
1235 | 1235 | ||
1236 | struct display *p = &fb_display[vc->vc_num]; | 1236 | struct display *p = &fb_display[vc->vc_num]; |
1237 | u_int y_break; | 1237 | u_int y_break; |
1238 | 1238 | ||
1239 | if (fbcon_is_inactive(vc, info)) | 1239 | if (fbcon_is_inactive(vc, info)) |
1240 | return; | 1240 | return; |
1241 | 1241 | ||
1242 | if (!height || !width) | 1242 | if (!height || !width) |
1243 | return; | 1243 | return; |
1244 | 1244 | ||
1245 | if (sy < vc->vc_top && vc->vc_top == logo_lines) | 1245 | if (sy < vc->vc_top && vc->vc_top == logo_lines) |
1246 | vc->vc_top = 0; | 1246 | vc->vc_top = 0; |
1247 | 1247 | ||
1248 | /* Split blits that cross physical y_wrap boundary */ | 1248 | /* Split blits that cross physical y_wrap boundary */ |
1249 | 1249 | ||
1250 | y_break = p->vrows - p->yscroll; | 1250 | y_break = p->vrows - p->yscroll; |
1251 | if (sy < y_break && sy + height - 1 >= y_break) { | 1251 | if (sy < y_break && sy + height - 1 >= y_break) { |
1252 | u_int b = y_break - sy; | 1252 | u_int b = y_break - sy; |
1253 | ops->clear(vc, info, real_y(p, sy), sx, b, width); | 1253 | ops->clear(vc, info, real_y(p, sy), sx, b, width); |
1254 | ops->clear(vc, info, real_y(p, sy + b), sx, height - b, | 1254 | ops->clear(vc, info, real_y(p, sy + b), sx, height - b, |
1255 | width); | 1255 | width); |
1256 | } else | 1256 | } else |
1257 | ops->clear(vc, info, real_y(p, sy), sx, height, width); | 1257 | ops->clear(vc, info, real_y(p, sy), sx, height, width); |
1258 | } | 1258 | } |
1259 | 1259 | ||
1260 | static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, | 1260 | static void fbcon_putcs(struct vc_data *vc, const unsigned short *s, |
1261 | int count, int ypos, int xpos) | 1261 | int count, int ypos, int xpos) |
1262 | { | 1262 | { |
1263 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 1263 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
1264 | struct display *p = &fb_display[vc->vc_num]; | 1264 | struct display *p = &fb_display[vc->vc_num]; |
1265 | struct fbcon_ops *ops = info->fbcon_par; | 1265 | struct fbcon_ops *ops = info->fbcon_par; |
1266 | 1266 | ||
1267 | if (!fbcon_is_inactive(vc, info)) | 1267 | if (!fbcon_is_inactive(vc, info)) |
1268 | ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, | 1268 | ops->putcs(vc, info, s, count, real_y(p, ypos), xpos, |
1269 | get_color(vc, info, scr_readw(s), 1), | 1269 | get_color(vc, info, scr_readw(s), 1), |
1270 | get_color(vc, info, scr_readw(s), 0)); | 1270 | get_color(vc, info, scr_readw(s), 0)); |
1271 | } | 1271 | } |
1272 | 1272 | ||
1273 | static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos) | 1273 | static void fbcon_putc(struct vc_data *vc, int c, int ypos, int xpos) |
1274 | { | 1274 | { |
1275 | unsigned short chr; | 1275 | unsigned short chr; |
1276 | 1276 | ||
1277 | scr_writew(c, &chr); | 1277 | scr_writew(c, &chr); |
1278 | fbcon_putcs(vc, &chr, 1, ypos, xpos); | 1278 | fbcon_putcs(vc, &chr, 1, ypos, xpos); |
1279 | } | 1279 | } |
1280 | 1280 | ||
1281 | static void fbcon_clear_margins(struct vc_data *vc, int bottom_only) | 1281 | static void fbcon_clear_margins(struct vc_data *vc, int bottom_only) |
1282 | { | 1282 | { |
1283 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 1283 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
1284 | struct fbcon_ops *ops = info->fbcon_par; | 1284 | struct fbcon_ops *ops = info->fbcon_par; |
1285 | 1285 | ||
1286 | if (!fbcon_is_inactive(vc, info)) | 1286 | if (!fbcon_is_inactive(vc, info)) |
1287 | ops->clear_margins(vc, info, bottom_only); | 1287 | ops->clear_margins(vc, info, bottom_only); |
1288 | } | 1288 | } |
1289 | 1289 | ||
1290 | static void fbcon_cursor(struct vc_data *vc, int mode) | 1290 | static void fbcon_cursor(struct vc_data *vc, int mode) |
1291 | { | 1291 | { |
1292 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 1292 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
1293 | struct fbcon_ops *ops = info->fbcon_par; | 1293 | struct fbcon_ops *ops = info->fbcon_par; |
1294 | int y; | 1294 | int y; |
1295 | int c = scr_readw((u16 *) vc->vc_pos); | 1295 | int c = scr_readw((u16 *) vc->vc_pos); |
1296 | 1296 | ||
1297 | if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1) | 1297 | if (fbcon_is_inactive(vc, info) || vc->vc_deccm != 1) |
1298 | return; | 1298 | return; |
1299 | 1299 | ||
1300 | if (vc->vc_cursor_type & 0x10) | 1300 | if (vc->vc_cursor_type & 0x10) |
1301 | fbcon_del_cursor_timer(info); | 1301 | fbcon_del_cursor_timer(info); |
1302 | else | 1302 | else |
1303 | fbcon_add_cursor_timer(info); | 1303 | fbcon_add_cursor_timer(info); |
1304 | 1304 | ||
1305 | ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1; | 1305 | ops->cursor_flash = (mode == CM_ERASE) ? 0 : 1; |
1306 | if (mode & CM_SOFTBACK) { | 1306 | if (mode & CM_SOFTBACK) { |
1307 | mode &= ~CM_SOFTBACK; | 1307 | mode &= ~CM_SOFTBACK; |
1308 | y = softback_lines; | 1308 | y = softback_lines; |
1309 | } else { | 1309 | } else { |
1310 | if (softback_lines) | 1310 | if (softback_lines) |
1311 | fbcon_set_origin(vc); | 1311 | fbcon_set_origin(vc); |
1312 | y = 0; | 1312 | y = 0; |
1313 | } | 1313 | } |
1314 | 1314 | ||
1315 | ops->cursor(vc, info, mode, y, get_color(vc, info, c, 1), | 1315 | ops->cursor(vc, info, mode, y, get_color(vc, info, c, 1), |
1316 | get_color(vc, info, c, 0)); | 1316 | get_color(vc, info, c, 0)); |
1317 | vbl_cursor_cnt = CURSOR_DRAW_DELAY; | 1317 | vbl_cursor_cnt = CURSOR_DRAW_DELAY; |
1318 | } | 1318 | } |
1319 | 1319 | ||
1320 | static int scrollback_phys_max = 0; | 1320 | static int scrollback_phys_max = 0; |
1321 | static int scrollback_max = 0; | 1321 | static int scrollback_max = 0; |
1322 | static int scrollback_current = 0; | 1322 | static int scrollback_current = 0; |
1323 | 1323 | ||
1324 | static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, | 1324 | static void fbcon_set_disp(struct fb_info *info, struct fb_var_screeninfo *var, |
1325 | int unit) | 1325 | int unit) |
1326 | { | 1326 | { |
1327 | struct display *p, *t; | 1327 | struct display *p, *t; |
1328 | struct vc_data **default_mode, *vc; | 1328 | struct vc_data **default_mode, *vc; |
1329 | struct vc_data *svc; | 1329 | struct vc_data *svc; |
1330 | struct fbcon_ops *ops = info->fbcon_par; | 1330 | struct fbcon_ops *ops = info->fbcon_par; |
1331 | int rows, cols, charcnt = 256; | 1331 | int rows, cols, charcnt = 256; |
1332 | 1332 | ||
1333 | p = &fb_display[unit]; | 1333 | p = &fb_display[unit]; |
1334 | 1334 | ||
1335 | if (var_to_display(p, var, info)) | 1335 | if (var_to_display(p, var, info)) |
1336 | return; | 1336 | return; |
1337 | 1337 | ||
1338 | vc = vc_cons[unit].d; | 1338 | vc = vc_cons[unit].d; |
1339 | 1339 | ||
1340 | if (!vc) | 1340 | if (!vc) |
1341 | return; | 1341 | return; |
1342 | 1342 | ||
1343 | default_mode = vc->vc_display_fg; | 1343 | default_mode = vc->vc_display_fg; |
1344 | svc = *default_mode; | 1344 | svc = *default_mode; |
1345 | t = &fb_display[svc->vc_num]; | 1345 | t = &fb_display[svc->vc_num]; |
1346 | 1346 | ||
1347 | if (!vc->vc_font.data) { | 1347 | if (!vc->vc_font.data) { |
1348 | vc->vc_font.data = (void *)(p->fontdata = t->fontdata); | 1348 | vc->vc_font.data = (void *)(p->fontdata = t->fontdata); |
1349 | vc->vc_font.width = (*default_mode)->vc_font.width; | 1349 | vc->vc_font.width = (*default_mode)->vc_font.width; |
1350 | vc->vc_font.height = (*default_mode)->vc_font.height; | 1350 | vc->vc_font.height = (*default_mode)->vc_font.height; |
1351 | p->userfont = t->userfont; | 1351 | p->userfont = t->userfont; |
1352 | if (p->userfont) | 1352 | if (p->userfont) |
1353 | REFCOUNT(p->fontdata)++; | 1353 | REFCOUNT(p->fontdata)++; |
1354 | } | 1354 | } |
1355 | if (p->userfont) | 1355 | if (p->userfont) |
1356 | charcnt = FNTCHARCNT(p->fontdata); | 1356 | charcnt = FNTCHARCNT(p->fontdata); |
1357 | 1357 | ||
1358 | var->activate = FB_ACTIVATE_NOW; | 1358 | var->activate = FB_ACTIVATE_NOW; |
1359 | info->var.activate = var->activate; | 1359 | info->var.activate = var->activate; |
1360 | var->yoffset = info->var.yoffset; | 1360 | var->yoffset = info->var.yoffset; |
1361 | var->xoffset = info->var.xoffset; | 1361 | var->xoffset = info->var.xoffset; |
1362 | fb_set_var(info, var); | 1362 | fb_set_var(info, var); |
1363 | ops->var = info->var; | 1363 | ops->var = info->var; |
1364 | vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); | 1364 | vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); |
1365 | vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; | 1365 | vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; |
1366 | if (charcnt == 256) { | 1366 | if (charcnt == 256) { |
1367 | vc->vc_hi_font_mask = 0; | 1367 | vc->vc_hi_font_mask = 0; |
1368 | } else { | 1368 | } else { |
1369 | vc->vc_hi_font_mask = 0x100; | 1369 | vc->vc_hi_font_mask = 0x100; |
1370 | if (vc->vc_can_do_color) | 1370 | if (vc->vc_can_do_color) |
1371 | vc->vc_complement_mask <<= 1; | 1371 | vc->vc_complement_mask <<= 1; |
1372 | } | 1372 | } |
1373 | 1373 | ||
1374 | if (!*svc->vc_uni_pagedir_loc) | 1374 | if (!*svc->vc_uni_pagedir_loc) |
1375 | con_set_default_unimap(svc); | 1375 | con_set_default_unimap(svc); |
1376 | if (!*vc->vc_uni_pagedir_loc) | 1376 | if (!*vc->vc_uni_pagedir_loc) |
1377 | con_copy_unimap(vc, svc); | 1377 | con_copy_unimap(vc, svc); |
1378 | 1378 | ||
1379 | cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); | 1379 | cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); |
1380 | rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); | 1380 | rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); |
1381 | cols /= vc->vc_font.width; | 1381 | cols /= vc->vc_font.width; |
1382 | rows /= vc->vc_font.height; | 1382 | rows /= vc->vc_font.height; |
1383 | vc_resize(vc, cols, rows); | 1383 | vc_resize(vc, cols, rows); |
1384 | 1384 | ||
1385 | if (CON_IS_VISIBLE(vc)) { | 1385 | if (CON_IS_VISIBLE(vc)) { |
1386 | update_screen(vc); | 1386 | update_screen(vc); |
1387 | if (softback_buf) | 1387 | if (softback_buf) |
1388 | fbcon_update_softback(vc); | 1388 | fbcon_update_softback(vc); |
1389 | } | 1389 | } |
1390 | } | 1390 | } |
1391 | 1391 | ||
1392 | static __inline__ void ywrap_up(struct vc_data *vc, int count) | 1392 | static __inline__ void ywrap_up(struct vc_data *vc, int count) |
1393 | { | 1393 | { |
1394 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 1394 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
1395 | struct fbcon_ops *ops = info->fbcon_par; | 1395 | struct fbcon_ops *ops = info->fbcon_par; |
1396 | struct display *p = &fb_display[vc->vc_num]; | 1396 | struct display *p = &fb_display[vc->vc_num]; |
1397 | 1397 | ||
1398 | p->yscroll += count; | 1398 | p->yscroll += count; |
1399 | if (p->yscroll >= p->vrows) /* Deal with wrap */ | 1399 | if (p->yscroll >= p->vrows) /* Deal with wrap */ |
1400 | p->yscroll -= p->vrows; | 1400 | p->yscroll -= p->vrows; |
1401 | ops->var.xoffset = 0; | 1401 | ops->var.xoffset = 0; |
1402 | ops->var.yoffset = p->yscroll * vc->vc_font.height; | 1402 | ops->var.yoffset = p->yscroll * vc->vc_font.height; |
1403 | ops->var.vmode |= FB_VMODE_YWRAP; | 1403 | ops->var.vmode |= FB_VMODE_YWRAP; |
1404 | ops->update_start(info); | 1404 | ops->update_start(info); |
1405 | scrollback_max += count; | 1405 | scrollback_max += count; |
1406 | if (scrollback_max > scrollback_phys_max) | 1406 | if (scrollback_max > scrollback_phys_max) |
1407 | scrollback_max = scrollback_phys_max; | 1407 | scrollback_max = scrollback_phys_max; |
1408 | scrollback_current = 0; | 1408 | scrollback_current = 0; |
1409 | } | 1409 | } |
1410 | 1410 | ||
1411 | static __inline__ void ywrap_down(struct vc_data *vc, int count) | 1411 | static __inline__ void ywrap_down(struct vc_data *vc, int count) |
1412 | { | 1412 | { |
1413 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 1413 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
1414 | struct fbcon_ops *ops = info->fbcon_par; | 1414 | struct fbcon_ops *ops = info->fbcon_par; |
1415 | struct display *p = &fb_display[vc->vc_num]; | 1415 | struct display *p = &fb_display[vc->vc_num]; |
1416 | 1416 | ||
1417 | p->yscroll -= count; | 1417 | p->yscroll -= count; |
1418 | if (p->yscroll < 0) /* Deal with wrap */ | 1418 | if (p->yscroll < 0) /* Deal with wrap */ |
1419 | p->yscroll += p->vrows; | 1419 | p->yscroll += p->vrows; |
1420 | ops->var.xoffset = 0; | 1420 | ops->var.xoffset = 0; |
1421 | ops->var.yoffset = p->yscroll * vc->vc_font.height; | 1421 | ops->var.yoffset = p->yscroll * vc->vc_font.height; |
1422 | ops->var.vmode |= FB_VMODE_YWRAP; | 1422 | ops->var.vmode |= FB_VMODE_YWRAP; |
1423 | ops->update_start(info); | 1423 | ops->update_start(info); |
1424 | scrollback_max -= count; | 1424 | scrollback_max -= count; |
1425 | if (scrollback_max < 0) | 1425 | if (scrollback_max < 0) |
1426 | scrollback_max = 0; | 1426 | scrollback_max = 0; |
1427 | scrollback_current = 0; | 1427 | scrollback_current = 0; |
1428 | } | 1428 | } |
1429 | 1429 | ||
1430 | static __inline__ void ypan_up(struct vc_data *vc, int count) | 1430 | static __inline__ void ypan_up(struct vc_data *vc, int count) |
1431 | { | 1431 | { |
1432 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 1432 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
1433 | struct display *p = &fb_display[vc->vc_num]; | 1433 | struct display *p = &fb_display[vc->vc_num]; |
1434 | struct fbcon_ops *ops = info->fbcon_par; | 1434 | struct fbcon_ops *ops = info->fbcon_par; |
1435 | 1435 | ||
1436 | p->yscroll += count; | 1436 | p->yscroll += count; |
1437 | if (p->yscroll > p->vrows - vc->vc_rows) { | 1437 | if (p->yscroll > p->vrows - vc->vc_rows) { |
1438 | ops->bmove(vc, info, p->vrows - vc->vc_rows, | 1438 | ops->bmove(vc, info, p->vrows - vc->vc_rows, |
1439 | 0, 0, 0, vc->vc_rows, vc->vc_cols); | 1439 | 0, 0, 0, vc->vc_rows, vc->vc_cols); |
1440 | p->yscroll -= p->vrows - vc->vc_rows; | 1440 | p->yscroll -= p->vrows - vc->vc_rows; |
1441 | } | 1441 | } |
1442 | 1442 | ||
1443 | ops->var.xoffset = 0; | 1443 | ops->var.xoffset = 0; |
1444 | ops->var.yoffset = p->yscroll * vc->vc_font.height; | 1444 | ops->var.yoffset = p->yscroll * vc->vc_font.height; |
1445 | ops->var.vmode &= ~FB_VMODE_YWRAP; | 1445 | ops->var.vmode &= ~FB_VMODE_YWRAP; |
1446 | ops->update_start(info); | 1446 | ops->update_start(info); |
1447 | fbcon_clear_margins(vc, 1); | 1447 | fbcon_clear_margins(vc, 1); |
1448 | scrollback_max += count; | 1448 | scrollback_max += count; |
1449 | if (scrollback_max > scrollback_phys_max) | 1449 | if (scrollback_max > scrollback_phys_max) |
1450 | scrollback_max = scrollback_phys_max; | 1450 | scrollback_max = scrollback_phys_max; |
1451 | scrollback_current = 0; | 1451 | scrollback_current = 0; |
1452 | } | 1452 | } |
1453 | 1453 | ||
1454 | static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count) | 1454 | static __inline__ void ypan_up_redraw(struct vc_data *vc, int t, int count) |
1455 | { | 1455 | { |
1456 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 1456 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
1457 | struct fbcon_ops *ops = info->fbcon_par; | 1457 | struct fbcon_ops *ops = info->fbcon_par; |
1458 | struct display *p = &fb_display[vc->vc_num]; | 1458 | struct display *p = &fb_display[vc->vc_num]; |
1459 | 1459 | ||
1460 | p->yscroll += count; | 1460 | p->yscroll += count; |
1461 | 1461 | ||
1462 | if (p->yscroll > p->vrows - vc->vc_rows) { | 1462 | if (p->yscroll > p->vrows - vc->vc_rows) { |
1463 | p->yscroll -= p->vrows - vc->vc_rows; | 1463 | p->yscroll -= p->vrows - vc->vc_rows; |
1464 | fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t); | 1464 | fbcon_redraw_move(vc, p, t + count, vc->vc_rows - count, t); |
1465 | } | 1465 | } |
1466 | 1466 | ||
1467 | ops->var.xoffset = 0; | 1467 | ops->var.xoffset = 0; |
1468 | ops->var.yoffset = p->yscroll * vc->vc_font.height; | 1468 | ops->var.yoffset = p->yscroll * vc->vc_font.height; |
1469 | ops->var.vmode &= ~FB_VMODE_YWRAP; | 1469 | ops->var.vmode &= ~FB_VMODE_YWRAP; |
1470 | ops->update_start(info); | 1470 | ops->update_start(info); |
1471 | fbcon_clear_margins(vc, 1); | 1471 | fbcon_clear_margins(vc, 1); |
1472 | scrollback_max += count; | 1472 | scrollback_max += count; |
1473 | if (scrollback_max > scrollback_phys_max) | 1473 | if (scrollback_max > scrollback_phys_max) |
1474 | scrollback_max = scrollback_phys_max; | 1474 | scrollback_max = scrollback_phys_max; |
1475 | scrollback_current = 0; | 1475 | scrollback_current = 0; |
1476 | } | 1476 | } |
1477 | 1477 | ||
1478 | static __inline__ void ypan_down(struct vc_data *vc, int count) | 1478 | static __inline__ void ypan_down(struct vc_data *vc, int count) |
1479 | { | 1479 | { |
1480 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 1480 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
1481 | struct display *p = &fb_display[vc->vc_num]; | 1481 | struct display *p = &fb_display[vc->vc_num]; |
1482 | struct fbcon_ops *ops = info->fbcon_par; | 1482 | struct fbcon_ops *ops = info->fbcon_par; |
1483 | 1483 | ||
1484 | p->yscroll -= count; | 1484 | p->yscroll -= count; |
1485 | if (p->yscroll < 0) { | 1485 | if (p->yscroll < 0) { |
1486 | ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows, | 1486 | ops->bmove(vc, info, 0, 0, p->vrows - vc->vc_rows, |
1487 | 0, vc->vc_rows, vc->vc_cols); | 1487 | 0, vc->vc_rows, vc->vc_cols); |
1488 | p->yscroll += p->vrows - vc->vc_rows; | 1488 | p->yscroll += p->vrows - vc->vc_rows; |
1489 | } | 1489 | } |
1490 | 1490 | ||
1491 | ops->var.xoffset = 0; | 1491 | ops->var.xoffset = 0; |
1492 | ops->var.yoffset = p->yscroll * vc->vc_font.height; | 1492 | ops->var.yoffset = p->yscroll * vc->vc_font.height; |
1493 | ops->var.vmode &= ~FB_VMODE_YWRAP; | 1493 | ops->var.vmode &= ~FB_VMODE_YWRAP; |
1494 | ops->update_start(info); | 1494 | ops->update_start(info); |
1495 | fbcon_clear_margins(vc, 1); | 1495 | fbcon_clear_margins(vc, 1); |
1496 | scrollback_max -= count; | 1496 | scrollback_max -= count; |
1497 | if (scrollback_max < 0) | 1497 | if (scrollback_max < 0) |
1498 | scrollback_max = 0; | 1498 | scrollback_max = 0; |
1499 | scrollback_current = 0; | 1499 | scrollback_current = 0; |
1500 | } | 1500 | } |
1501 | 1501 | ||
1502 | static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) | 1502 | static __inline__ void ypan_down_redraw(struct vc_data *vc, int t, int count) |
1503 | { | 1503 | { |
1504 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 1504 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
1505 | struct fbcon_ops *ops = info->fbcon_par; | 1505 | struct fbcon_ops *ops = info->fbcon_par; |
1506 | struct display *p = &fb_display[vc->vc_num]; | 1506 | struct display *p = &fb_display[vc->vc_num]; |
1507 | 1507 | ||
1508 | p->yscroll -= count; | 1508 | p->yscroll -= count; |
1509 | 1509 | ||
1510 | if (p->yscroll < 0) { | 1510 | if (p->yscroll < 0) { |
1511 | p->yscroll += p->vrows - vc->vc_rows; | 1511 | p->yscroll += p->vrows - vc->vc_rows; |
1512 | fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count); | 1512 | fbcon_redraw_move(vc, p, t, vc->vc_rows - count, t + count); |
1513 | } | 1513 | } |
1514 | 1514 | ||
1515 | ops->var.xoffset = 0; | 1515 | ops->var.xoffset = 0; |
1516 | ops->var.yoffset = p->yscroll * vc->vc_font.height; | 1516 | ops->var.yoffset = p->yscroll * vc->vc_font.height; |
1517 | ops->var.vmode &= ~FB_VMODE_YWRAP; | 1517 | ops->var.vmode &= ~FB_VMODE_YWRAP; |
1518 | ops->update_start(info); | 1518 | ops->update_start(info); |
1519 | fbcon_clear_margins(vc, 1); | 1519 | fbcon_clear_margins(vc, 1); |
1520 | scrollback_max -= count; | 1520 | scrollback_max -= count; |
1521 | if (scrollback_max < 0) | 1521 | if (scrollback_max < 0) |
1522 | scrollback_max = 0; | 1522 | scrollback_max = 0; |
1523 | scrollback_current = 0; | 1523 | scrollback_current = 0; |
1524 | } | 1524 | } |
1525 | 1525 | ||
1526 | static void fbcon_redraw_softback(struct vc_data *vc, struct display *p, | 1526 | static void fbcon_redraw_softback(struct vc_data *vc, struct display *p, |
1527 | long delta) | 1527 | long delta) |
1528 | { | 1528 | { |
1529 | int count = vc->vc_rows; | 1529 | int count = vc->vc_rows; |
1530 | unsigned short *d, *s; | 1530 | unsigned short *d, *s; |
1531 | unsigned long n; | 1531 | unsigned long n; |
1532 | int line = 0; | 1532 | int line = 0; |
1533 | 1533 | ||
1534 | d = (u16 *) softback_curr; | 1534 | d = (u16 *) softback_curr; |
1535 | if (d == (u16 *) softback_in) | 1535 | if (d == (u16 *) softback_in) |
1536 | d = (u16 *) vc->vc_origin; | 1536 | d = (u16 *) vc->vc_origin; |
1537 | n = softback_curr + delta * vc->vc_size_row; | 1537 | n = softback_curr + delta * vc->vc_size_row; |
1538 | softback_lines -= delta; | 1538 | softback_lines -= delta; |
1539 | if (delta < 0) { | 1539 | if (delta < 0) { |
1540 | if (softback_curr < softback_top && n < softback_buf) { | 1540 | if (softback_curr < softback_top && n < softback_buf) { |
1541 | n += softback_end - softback_buf; | 1541 | n += softback_end - softback_buf; |
1542 | if (n < softback_top) { | 1542 | if (n < softback_top) { |
1543 | softback_lines -= | 1543 | softback_lines -= |
1544 | (softback_top - n) / vc->vc_size_row; | 1544 | (softback_top - n) / vc->vc_size_row; |
1545 | n = softback_top; | 1545 | n = softback_top; |
1546 | } | 1546 | } |
1547 | } else if (softback_curr >= softback_top | 1547 | } else if (softback_curr >= softback_top |
1548 | && n < softback_top) { | 1548 | && n < softback_top) { |
1549 | softback_lines -= | 1549 | softback_lines -= |
1550 | (softback_top - n) / vc->vc_size_row; | 1550 | (softback_top - n) / vc->vc_size_row; |
1551 | n = softback_top; | 1551 | n = softback_top; |
1552 | } | 1552 | } |
1553 | } else { | 1553 | } else { |
1554 | if (softback_curr > softback_in && n >= softback_end) { | 1554 | if (softback_curr > softback_in && n >= softback_end) { |
1555 | n += softback_buf - softback_end; | 1555 | n += softback_buf - softback_end; |
1556 | if (n > softback_in) { | 1556 | if (n > softback_in) { |
1557 | n = softback_in; | 1557 | n = softback_in; |
1558 | softback_lines = 0; | 1558 | softback_lines = 0; |
1559 | } | 1559 | } |
1560 | } else if (softback_curr <= softback_in && n > softback_in) { | 1560 | } else if (softback_curr <= softback_in && n > softback_in) { |
1561 | n = softback_in; | 1561 | n = softback_in; |
1562 | softback_lines = 0; | 1562 | softback_lines = 0; |
1563 | } | 1563 | } |
1564 | } | 1564 | } |
1565 | if (n == softback_curr) | 1565 | if (n == softback_curr) |
1566 | return; | 1566 | return; |
1567 | softback_curr = n; | 1567 | softback_curr = n; |
1568 | s = (u16 *) softback_curr; | 1568 | s = (u16 *) softback_curr; |
1569 | if (s == (u16 *) softback_in) | 1569 | if (s == (u16 *) softback_in) |
1570 | s = (u16 *) vc->vc_origin; | 1570 | s = (u16 *) vc->vc_origin; |
1571 | while (count--) { | 1571 | while (count--) { |
1572 | unsigned short *start; | 1572 | unsigned short *start; |
1573 | unsigned short *le; | 1573 | unsigned short *le; |
1574 | unsigned short c; | 1574 | unsigned short c; |
1575 | int x = 0; | 1575 | int x = 0; |
1576 | unsigned short attr = 1; | 1576 | unsigned short attr = 1; |
1577 | 1577 | ||
1578 | start = s; | 1578 | start = s; |
1579 | le = advance_row(s, 1); | 1579 | le = advance_row(s, 1); |
1580 | do { | 1580 | do { |
1581 | c = scr_readw(s); | 1581 | c = scr_readw(s); |
1582 | if (attr != (c & 0xff00)) { | 1582 | if (attr != (c & 0xff00)) { |
1583 | attr = c & 0xff00; | 1583 | attr = c & 0xff00; |
1584 | if (s > start) { | 1584 | if (s > start) { |
1585 | fbcon_putcs(vc, start, s - start, | 1585 | fbcon_putcs(vc, start, s - start, |
1586 | line, x); | 1586 | line, x); |
1587 | x += s - start; | 1587 | x += s - start; |
1588 | start = s; | 1588 | start = s; |
1589 | } | 1589 | } |
1590 | } | 1590 | } |
1591 | if (c == scr_readw(d)) { | 1591 | if (c == scr_readw(d)) { |
1592 | if (s > start) { | 1592 | if (s > start) { |
1593 | fbcon_putcs(vc, start, s - start, | 1593 | fbcon_putcs(vc, start, s - start, |
1594 | line, x); | 1594 | line, x); |
1595 | x += s - start + 1; | 1595 | x += s - start + 1; |
1596 | start = s + 1; | 1596 | start = s + 1; |
1597 | } else { | 1597 | } else { |
1598 | x++; | 1598 | x++; |
1599 | start++; | 1599 | start++; |
1600 | } | 1600 | } |
1601 | } | 1601 | } |
1602 | s++; | 1602 | s++; |
1603 | d++; | 1603 | d++; |
1604 | } while (s < le); | 1604 | } while (s < le); |
1605 | if (s > start) | 1605 | if (s > start) |
1606 | fbcon_putcs(vc, start, s - start, line, x); | 1606 | fbcon_putcs(vc, start, s - start, line, x); |
1607 | line++; | 1607 | line++; |
1608 | if (d == (u16 *) softback_end) | 1608 | if (d == (u16 *) softback_end) |
1609 | d = (u16 *) softback_buf; | 1609 | d = (u16 *) softback_buf; |
1610 | if (d == (u16 *) softback_in) | 1610 | if (d == (u16 *) softback_in) |
1611 | d = (u16 *) vc->vc_origin; | 1611 | d = (u16 *) vc->vc_origin; |
1612 | if (s == (u16 *) softback_end) | 1612 | if (s == (u16 *) softback_end) |
1613 | s = (u16 *) softback_buf; | 1613 | s = (u16 *) softback_buf; |
1614 | if (s == (u16 *) softback_in) | 1614 | if (s == (u16 *) softback_in) |
1615 | s = (u16 *) vc->vc_origin; | 1615 | s = (u16 *) vc->vc_origin; |
1616 | } | 1616 | } |
1617 | } | 1617 | } |
1618 | 1618 | ||
1619 | static void fbcon_redraw_move(struct vc_data *vc, struct display *p, | 1619 | static void fbcon_redraw_move(struct vc_data *vc, struct display *p, |
1620 | int line, int count, int dy) | 1620 | int line, int count, int dy) |
1621 | { | 1621 | { |
1622 | unsigned short *s = (unsigned short *) | 1622 | unsigned short *s = (unsigned short *) |
1623 | (vc->vc_origin + vc->vc_size_row * line); | 1623 | (vc->vc_origin + vc->vc_size_row * line); |
1624 | 1624 | ||
1625 | while (count--) { | 1625 | while (count--) { |
1626 | unsigned short *start = s; | 1626 | unsigned short *start = s; |
1627 | unsigned short *le = advance_row(s, 1); | 1627 | unsigned short *le = advance_row(s, 1); |
1628 | unsigned short c; | 1628 | unsigned short c; |
1629 | int x = 0; | 1629 | int x = 0; |
1630 | unsigned short attr = 1; | 1630 | unsigned short attr = 1; |
1631 | 1631 | ||
1632 | do { | 1632 | do { |
1633 | c = scr_readw(s); | 1633 | c = scr_readw(s); |
1634 | if (attr != (c & 0xff00)) { | 1634 | if (attr != (c & 0xff00)) { |
1635 | attr = c & 0xff00; | 1635 | attr = c & 0xff00; |
1636 | if (s > start) { | 1636 | if (s > start) { |
1637 | fbcon_putcs(vc, start, s - start, | 1637 | fbcon_putcs(vc, start, s - start, |
1638 | dy, x); | 1638 | dy, x); |
1639 | x += s - start; | 1639 | x += s - start; |
1640 | start = s; | 1640 | start = s; |
1641 | } | 1641 | } |
1642 | } | 1642 | } |
1643 | console_conditional_schedule(); | 1643 | console_conditional_schedule(); |
1644 | s++; | 1644 | s++; |
1645 | } while (s < le); | 1645 | } while (s < le); |
1646 | if (s > start) | 1646 | if (s > start) |
1647 | fbcon_putcs(vc, start, s - start, dy, x); | 1647 | fbcon_putcs(vc, start, s - start, dy, x); |
1648 | console_conditional_schedule(); | 1648 | console_conditional_schedule(); |
1649 | dy++; | 1649 | dy++; |
1650 | } | 1650 | } |
1651 | } | 1651 | } |
1652 | 1652 | ||
1653 | static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info, | 1653 | static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info, |
1654 | struct display *p, int line, int count, int ycount) | 1654 | struct display *p, int line, int count, int ycount) |
1655 | { | 1655 | { |
1656 | int offset = ycount * vc->vc_cols; | 1656 | int offset = ycount * vc->vc_cols; |
1657 | unsigned short *d = (unsigned short *) | 1657 | unsigned short *d = (unsigned short *) |
1658 | (vc->vc_origin + vc->vc_size_row * line); | 1658 | (vc->vc_origin + vc->vc_size_row * line); |
1659 | unsigned short *s = d + offset; | 1659 | unsigned short *s = d + offset; |
1660 | struct fbcon_ops *ops = info->fbcon_par; | 1660 | struct fbcon_ops *ops = info->fbcon_par; |
1661 | 1661 | ||
1662 | while (count--) { | 1662 | while (count--) { |
1663 | unsigned short *start = s; | 1663 | unsigned short *start = s; |
1664 | unsigned short *le = advance_row(s, 1); | 1664 | unsigned short *le = advance_row(s, 1); |
1665 | unsigned short c; | 1665 | unsigned short c; |
1666 | int x = 0; | 1666 | int x = 0; |
1667 | 1667 | ||
1668 | do { | 1668 | do { |
1669 | c = scr_readw(s); | 1669 | c = scr_readw(s); |
1670 | 1670 | ||
1671 | if (c == scr_readw(d)) { | 1671 | if (c == scr_readw(d)) { |
1672 | if (s > start) { | 1672 | if (s > start) { |
1673 | ops->bmove(vc, info, line + ycount, x, | 1673 | ops->bmove(vc, info, line + ycount, x, |
1674 | line, x, 1, s-start); | 1674 | line, x, 1, s-start); |
1675 | x += s - start + 1; | 1675 | x += s - start + 1; |
1676 | start = s + 1; | 1676 | start = s + 1; |
1677 | } else { | 1677 | } else { |
1678 | x++; | 1678 | x++; |
1679 | start++; | 1679 | start++; |
1680 | } | 1680 | } |
1681 | } | 1681 | } |
1682 | 1682 | ||
1683 | scr_writew(c, d); | 1683 | scr_writew(c, d); |
1684 | console_conditional_schedule(); | 1684 | console_conditional_schedule(); |
1685 | s++; | 1685 | s++; |
1686 | d++; | 1686 | d++; |
1687 | } while (s < le); | 1687 | } while (s < le); |
1688 | if (s > start) | 1688 | if (s > start) |
1689 | ops->bmove(vc, info, line + ycount, x, line, x, 1, | 1689 | ops->bmove(vc, info, line + ycount, x, line, x, 1, |
1690 | s-start); | 1690 | s-start); |
1691 | console_conditional_schedule(); | 1691 | console_conditional_schedule(); |
1692 | if (ycount > 0) | 1692 | if (ycount > 0) |
1693 | line++; | 1693 | line++; |
1694 | else { | 1694 | else { |
1695 | line--; | 1695 | line--; |
1696 | /* NOTE: We subtract two lines from these pointers */ | 1696 | /* NOTE: We subtract two lines from these pointers */ |
1697 | s -= vc->vc_size_row; | 1697 | s -= vc->vc_size_row; |
1698 | d -= vc->vc_size_row; | 1698 | d -= vc->vc_size_row; |
1699 | } | 1699 | } |
1700 | } | 1700 | } |
1701 | } | 1701 | } |
1702 | 1702 | ||
1703 | static void fbcon_redraw(struct vc_data *vc, struct display *p, | 1703 | static void fbcon_redraw(struct vc_data *vc, struct display *p, |
1704 | int line, int count, int offset) | 1704 | int line, int count, int offset) |
1705 | { | 1705 | { |
1706 | unsigned short *d = (unsigned short *) | 1706 | unsigned short *d = (unsigned short *) |
1707 | (vc->vc_origin + vc->vc_size_row * line); | 1707 | (vc->vc_origin + vc->vc_size_row * line); |
1708 | unsigned short *s = d + offset; | 1708 | unsigned short *s = d + offset; |
1709 | 1709 | ||
1710 | while (count--) { | 1710 | while (count--) { |
1711 | unsigned short *start = s; | 1711 | unsigned short *start = s; |
1712 | unsigned short *le = advance_row(s, 1); | 1712 | unsigned short *le = advance_row(s, 1); |
1713 | unsigned short c; | 1713 | unsigned short c; |
1714 | int x = 0; | 1714 | int x = 0; |
1715 | unsigned short attr = 1; | 1715 | unsigned short attr = 1; |
1716 | 1716 | ||
1717 | do { | 1717 | do { |
1718 | c = scr_readw(s); | 1718 | c = scr_readw(s); |
1719 | if (attr != (c & 0xff00)) { | 1719 | if (attr != (c & 0xff00)) { |
1720 | attr = c & 0xff00; | 1720 | attr = c & 0xff00; |
1721 | if (s > start) { | 1721 | if (s > start) { |
1722 | fbcon_putcs(vc, start, s - start, | 1722 | fbcon_putcs(vc, start, s - start, |
1723 | line, x); | 1723 | line, x); |
1724 | x += s - start; | 1724 | x += s - start; |
1725 | start = s; | 1725 | start = s; |
1726 | } | 1726 | } |
1727 | } | 1727 | } |
1728 | if (c == scr_readw(d)) { | 1728 | if (c == scr_readw(d)) { |
1729 | if (s > start) { | 1729 | if (s > start) { |
1730 | fbcon_putcs(vc, start, s - start, | 1730 | fbcon_putcs(vc, start, s - start, |
1731 | line, x); | 1731 | line, x); |
1732 | x += s - start + 1; | 1732 | x += s - start + 1; |
1733 | start = s + 1; | 1733 | start = s + 1; |
1734 | } else { | 1734 | } else { |
1735 | x++; | 1735 | x++; |
1736 | start++; | 1736 | start++; |
1737 | } | 1737 | } |
1738 | } | 1738 | } |
1739 | scr_writew(c, d); | 1739 | scr_writew(c, d); |
1740 | console_conditional_schedule(); | 1740 | console_conditional_schedule(); |
1741 | s++; | 1741 | s++; |
1742 | d++; | 1742 | d++; |
1743 | } while (s < le); | 1743 | } while (s < le); |
1744 | if (s > start) | 1744 | if (s > start) |
1745 | fbcon_putcs(vc, start, s - start, line, x); | 1745 | fbcon_putcs(vc, start, s - start, line, x); |
1746 | console_conditional_schedule(); | 1746 | console_conditional_schedule(); |
1747 | if (offset > 0) | 1747 | if (offset > 0) |
1748 | line++; | 1748 | line++; |
1749 | else { | 1749 | else { |
1750 | line--; | 1750 | line--; |
1751 | /* NOTE: We subtract two lines from these pointers */ | 1751 | /* NOTE: We subtract two lines from these pointers */ |
1752 | s -= vc->vc_size_row; | 1752 | s -= vc->vc_size_row; |
1753 | d -= vc->vc_size_row; | 1753 | d -= vc->vc_size_row; |
1754 | } | 1754 | } |
1755 | } | 1755 | } |
1756 | } | 1756 | } |
1757 | 1757 | ||
1758 | static inline void fbcon_softback_note(struct vc_data *vc, int t, | 1758 | static inline void fbcon_softback_note(struct vc_data *vc, int t, |
1759 | int count) | 1759 | int count) |
1760 | { | 1760 | { |
1761 | unsigned short *p; | 1761 | unsigned short *p; |
1762 | 1762 | ||
1763 | if (vc->vc_num != fg_console) | 1763 | if (vc->vc_num != fg_console) |
1764 | return; | 1764 | return; |
1765 | p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row); | 1765 | p = (unsigned short *) (vc->vc_origin + t * vc->vc_size_row); |
1766 | 1766 | ||
1767 | while (count) { | 1767 | while (count) { |
1768 | scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row); | 1768 | scr_memcpyw((u16 *) softback_in, p, vc->vc_size_row); |
1769 | count--; | 1769 | count--; |
1770 | p = advance_row(p, 1); | 1770 | p = advance_row(p, 1); |
1771 | softback_in += vc->vc_size_row; | 1771 | softback_in += vc->vc_size_row; |
1772 | if (softback_in == softback_end) | 1772 | if (softback_in == softback_end) |
1773 | softback_in = softback_buf; | 1773 | softback_in = softback_buf; |
1774 | if (softback_in == softback_top) { | 1774 | if (softback_in == softback_top) { |
1775 | softback_top += vc->vc_size_row; | 1775 | softback_top += vc->vc_size_row; |
1776 | if (softback_top == softback_end) | 1776 | if (softback_top == softback_end) |
1777 | softback_top = softback_buf; | 1777 | softback_top = softback_buf; |
1778 | } | 1778 | } |
1779 | } | 1779 | } |
1780 | softback_curr = softback_in; | 1780 | softback_curr = softback_in; |
1781 | } | 1781 | } |
1782 | 1782 | ||
1783 | static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, | 1783 | static int fbcon_scroll(struct vc_data *vc, int t, int b, int dir, |
1784 | int count) | 1784 | int count) |
1785 | { | 1785 | { |
1786 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 1786 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
1787 | struct display *p = &fb_display[vc->vc_num]; | 1787 | struct display *p = &fb_display[vc->vc_num]; |
1788 | int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; | 1788 | int scroll_partial = info->flags & FBINFO_PARTIAL_PAN_OK; |
1789 | 1789 | ||
1790 | if (fbcon_is_inactive(vc, info)) | 1790 | if (fbcon_is_inactive(vc, info)) |
1791 | return -EINVAL; | 1791 | return -EINVAL; |
1792 | 1792 | ||
1793 | fbcon_cursor(vc, CM_ERASE); | 1793 | fbcon_cursor(vc, CM_ERASE); |
1794 | 1794 | ||
1795 | /* | 1795 | /* |
1796 | * ++Geert: Only use ywrap/ypan if the console is in text mode | 1796 | * ++Geert: Only use ywrap/ypan if the console is in text mode |
1797 | * ++Andrew: Only use ypan on hardware text mode when scrolling the | 1797 | * ++Andrew: Only use ypan on hardware text mode when scrolling the |
1798 | * whole screen (prevents flicker). | 1798 | * whole screen (prevents flicker). |
1799 | */ | 1799 | */ |
1800 | 1800 | ||
1801 | switch (dir) { | 1801 | switch (dir) { |
1802 | case SM_UP: | 1802 | case SM_UP: |
1803 | if (count > vc->vc_rows) /* Maximum realistic size */ | 1803 | if (count > vc->vc_rows) /* Maximum realistic size */ |
1804 | count = vc->vc_rows; | 1804 | count = vc->vc_rows; |
1805 | if (softback_top) | 1805 | if (softback_top) |
1806 | fbcon_softback_note(vc, t, count); | 1806 | fbcon_softback_note(vc, t, count); |
1807 | if (logo_shown >= 0) | 1807 | if (logo_shown >= 0) |
1808 | goto redraw_up; | 1808 | goto redraw_up; |
1809 | switch (p->scrollmode) { | 1809 | switch (p->scrollmode) { |
1810 | case SCROLL_MOVE: | 1810 | case SCROLL_MOVE: |
1811 | fbcon_redraw_blit(vc, info, p, t, b - t - count, | 1811 | fbcon_redraw_blit(vc, info, p, t, b - t - count, |
1812 | count); | 1812 | count); |
1813 | fbcon_clear(vc, b - count, 0, count, vc->vc_cols); | 1813 | fbcon_clear(vc, b - count, 0, count, vc->vc_cols); |
1814 | scr_memsetw((unsigned short *) (vc->vc_origin + | 1814 | scr_memsetw((unsigned short *) (vc->vc_origin + |
1815 | vc->vc_size_row * | 1815 | vc->vc_size_row * |
1816 | (b - count)), | 1816 | (b - count)), |
1817 | vc->vc_video_erase_char, | 1817 | vc->vc_video_erase_char, |
1818 | vc->vc_size_row * count); | 1818 | vc->vc_size_row * count); |
1819 | return 1; | 1819 | return 1; |
1820 | break; | 1820 | break; |
1821 | 1821 | ||
1822 | case SCROLL_WRAP_MOVE: | 1822 | case SCROLL_WRAP_MOVE: |
1823 | if (b - t - count > 3 * vc->vc_rows >> 2) { | 1823 | if (b - t - count > 3 * vc->vc_rows >> 2) { |
1824 | if (t > 0) | 1824 | if (t > 0) |
1825 | fbcon_bmove(vc, 0, 0, count, 0, t, | 1825 | fbcon_bmove(vc, 0, 0, count, 0, t, |
1826 | vc->vc_cols); | 1826 | vc->vc_cols); |
1827 | ywrap_up(vc, count); | 1827 | ywrap_up(vc, count); |
1828 | if (vc->vc_rows - b > 0) | 1828 | if (vc->vc_rows - b > 0) |
1829 | fbcon_bmove(vc, b - count, 0, b, 0, | 1829 | fbcon_bmove(vc, b - count, 0, b, 0, |
1830 | vc->vc_rows - b, | 1830 | vc->vc_rows - b, |
1831 | vc->vc_cols); | 1831 | vc->vc_cols); |
1832 | } else if (info->flags & FBINFO_READS_FAST) | 1832 | } else if (info->flags & FBINFO_READS_FAST) |
1833 | fbcon_bmove(vc, t + count, 0, t, 0, | 1833 | fbcon_bmove(vc, t + count, 0, t, 0, |
1834 | b - t - count, vc->vc_cols); | 1834 | b - t - count, vc->vc_cols); |
1835 | else | 1835 | else |
1836 | goto redraw_up; | 1836 | goto redraw_up; |
1837 | fbcon_clear(vc, b - count, 0, count, vc->vc_cols); | 1837 | fbcon_clear(vc, b - count, 0, count, vc->vc_cols); |
1838 | break; | 1838 | break; |
1839 | 1839 | ||
1840 | case SCROLL_PAN_REDRAW: | 1840 | case SCROLL_PAN_REDRAW: |
1841 | if ((p->yscroll + count <= | 1841 | if ((p->yscroll + count <= |
1842 | 2 * (p->vrows - vc->vc_rows)) | 1842 | 2 * (p->vrows - vc->vc_rows)) |
1843 | && ((!scroll_partial && (b - t == vc->vc_rows)) | 1843 | && ((!scroll_partial && (b - t == vc->vc_rows)) |
1844 | || (scroll_partial | 1844 | || (scroll_partial |
1845 | && (b - t - count > | 1845 | && (b - t - count > |
1846 | 3 * vc->vc_rows >> 2)))) { | 1846 | 3 * vc->vc_rows >> 2)))) { |
1847 | if (t > 0) | 1847 | if (t > 0) |
1848 | fbcon_redraw_move(vc, p, 0, t, count); | 1848 | fbcon_redraw_move(vc, p, 0, t, count); |
1849 | ypan_up_redraw(vc, t, count); | 1849 | ypan_up_redraw(vc, t, count); |
1850 | if (vc->vc_rows - b > 0) | 1850 | if (vc->vc_rows - b > 0) |
1851 | fbcon_redraw_move(vc, p, b, | 1851 | fbcon_redraw_move(vc, p, b, |
1852 | vc->vc_rows - b, b); | 1852 | vc->vc_rows - b, b); |
1853 | } else | 1853 | } else |
1854 | fbcon_redraw_move(vc, p, t + count, b - t - count, t); | 1854 | fbcon_redraw_move(vc, p, t + count, b - t - count, t); |
1855 | fbcon_clear(vc, b - count, 0, count, vc->vc_cols); | 1855 | fbcon_clear(vc, b - count, 0, count, vc->vc_cols); |
1856 | break; | 1856 | break; |
1857 | 1857 | ||
1858 | case SCROLL_PAN_MOVE: | 1858 | case SCROLL_PAN_MOVE: |
1859 | if ((p->yscroll + count <= | 1859 | if ((p->yscroll + count <= |
1860 | 2 * (p->vrows - vc->vc_rows)) | 1860 | 2 * (p->vrows - vc->vc_rows)) |
1861 | && ((!scroll_partial && (b - t == vc->vc_rows)) | 1861 | && ((!scroll_partial && (b - t == vc->vc_rows)) |
1862 | || (scroll_partial | 1862 | || (scroll_partial |
1863 | && (b - t - count > | 1863 | && (b - t - count > |
1864 | 3 * vc->vc_rows >> 2)))) { | 1864 | 3 * vc->vc_rows >> 2)))) { |
1865 | if (t > 0) | 1865 | if (t > 0) |
1866 | fbcon_bmove(vc, 0, 0, count, 0, t, | 1866 | fbcon_bmove(vc, 0, 0, count, 0, t, |
1867 | vc->vc_cols); | 1867 | vc->vc_cols); |
1868 | ypan_up(vc, count); | 1868 | ypan_up(vc, count); |
1869 | if (vc->vc_rows - b > 0) | 1869 | if (vc->vc_rows - b > 0) |
1870 | fbcon_bmove(vc, b - count, 0, b, 0, | 1870 | fbcon_bmove(vc, b - count, 0, b, 0, |
1871 | vc->vc_rows - b, | 1871 | vc->vc_rows - b, |
1872 | vc->vc_cols); | 1872 | vc->vc_cols); |
1873 | } else if (info->flags & FBINFO_READS_FAST) | 1873 | } else if (info->flags & FBINFO_READS_FAST) |
1874 | fbcon_bmove(vc, t + count, 0, t, 0, | 1874 | fbcon_bmove(vc, t + count, 0, t, 0, |
1875 | b - t - count, vc->vc_cols); | 1875 | b - t - count, vc->vc_cols); |
1876 | else | 1876 | else |
1877 | goto redraw_up; | 1877 | goto redraw_up; |
1878 | fbcon_clear(vc, b - count, 0, count, vc->vc_cols); | 1878 | fbcon_clear(vc, b - count, 0, count, vc->vc_cols); |
1879 | break; | 1879 | break; |
1880 | 1880 | ||
1881 | case SCROLL_REDRAW: | 1881 | case SCROLL_REDRAW: |
1882 | redraw_up: | 1882 | redraw_up: |
1883 | fbcon_redraw(vc, p, t, b - t - count, | 1883 | fbcon_redraw(vc, p, t, b - t - count, |
1884 | count * vc->vc_cols); | 1884 | count * vc->vc_cols); |
1885 | fbcon_clear(vc, b - count, 0, count, vc->vc_cols); | 1885 | fbcon_clear(vc, b - count, 0, count, vc->vc_cols); |
1886 | scr_memsetw((unsigned short *) (vc->vc_origin + | 1886 | scr_memsetw((unsigned short *) (vc->vc_origin + |
1887 | vc->vc_size_row * | 1887 | vc->vc_size_row * |
1888 | (b - count)), | 1888 | (b - count)), |
1889 | vc->vc_video_erase_char, | 1889 | vc->vc_video_erase_char, |
1890 | vc->vc_size_row * count); | 1890 | vc->vc_size_row * count); |
1891 | return 1; | 1891 | return 1; |
1892 | } | 1892 | } |
1893 | break; | 1893 | break; |
1894 | 1894 | ||
1895 | case SM_DOWN: | 1895 | case SM_DOWN: |
1896 | if (count > vc->vc_rows) /* Maximum realistic size */ | 1896 | if (count > vc->vc_rows) /* Maximum realistic size */ |
1897 | count = vc->vc_rows; | 1897 | count = vc->vc_rows; |
1898 | if (logo_shown >= 0) | 1898 | if (logo_shown >= 0) |
1899 | goto redraw_down; | 1899 | goto redraw_down; |
1900 | switch (p->scrollmode) { | 1900 | switch (p->scrollmode) { |
1901 | case SCROLL_MOVE: | 1901 | case SCROLL_MOVE: |
1902 | fbcon_redraw_blit(vc, info, p, b - 1, b - t - count, | 1902 | fbcon_redraw_blit(vc, info, p, b - 1, b - t - count, |
1903 | -count); | 1903 | -count); |
1904 | fbcon_clear(vc, t, 0, count, vc->vc_cols); | 1904 | fbcon_clear(vc, t, 0, count, vc->vc_cols); |
1905 | scr_memsetw((unsigned short *) (vc->vc_origin + | 1905 | scr_memsetw((unsigned short *) (vc->vc_origin + |
1906 | vc->vc_size_row * | 1906 | vc->vc_size_row * |
1907 | t), | 1907 | t), |
1908 | vc->vc_video_erase_char, | 1908 | vc->vc_video_erase_char, |
1909 | vc->vc_size_row * count); | 1909 | vc->vc_size_row * count); |
1910 | return 1; | 1910 | return 1; |
1911 | break; | 1911 | break; |
1912 | 1912 | ||
1913 | case SCROLL_WRAP_MOVE: | 1913 | case SCROLL_WRAP_MOVE: |
1914 | if (b - t - count > 3 * vc->vc_rows >> 2) { | 1914 | if (b - t - count > 3 * vc->vc_rows >> 2) { |
1915 | if (vc->vc_rows - b > 0) | 1915 | if (vc->vc_rows - b > 0) |
1916 | fbcon_bmove(vc, b, 0, b - count, 0, | 1916 | fbcon_bmove(vc, b, 0, b - count, 0, |
1917 | vc->vc_rows - b, | 1917 | vc->vc_rows - b, |
1918 | vc->vc_cols); | 1918 | vc->vc_cols); |
1919 | ywrap_down(vc, count); | 1919 | ywrap_down(vc, count); |
1920 | if (t > 0) | 1920 | if (t > 0) |
1921 | fbcon_bmove(vc, count, 0, 0, 0, t, | 1921 | fbcon_bmove(vc, count, 0, 0, 0, t, |
1922 | vc->vc_cols); | 1922 | vc->vc_cols); |
1923 | } else if (info->flags & FBINFO_READS_FAST) | 1923 | } else if (info->flags & FBINFO_READS_FAST) |
1924 | fbcon_bmove(vc, t, 0, t + count, 0, | 1924 | fbcon_bmove(vc, t, 0, t + count, 0, |
1925 | b - t - count, vc->vc_cols); | 1925 | b - t - count, vc->vc_cols); |
1926 | else | 1926 | else |
1927 | goto redraw_down; | 1927 | goto redraw_down; |
1928 | fbcon_clear(vc, t, 0, count, vc->vc_cols); | 1928 | fbcon_clear(vc, t, 0, count, vc->vc_cols); |
1929 | break; | 1929 | break; |
1930 | 1930 | ||
1931 | case SCROLL_PAN_MOVE: | 1931 | case SCROLL_PAN_MOVE: |
1932 | if ((count - p->yscroll <= p->vrows - vc->vc_rows) | 1932 | if ((count - p->yscroll <= p->vrows - vc->vc_rows) |
1933 | && ((!scroll_partial && (b - t == vc->vc_rows)) | 1933 | && ((!scroll_partial && (b - t == vc->vc_rows)) |
1934 | || (scroll_partial | 1934 | || (scroll_partial |
1935 | && (b - t - count > | 1935 | && (b - t - count > |
1936 | 3 * vc->vc_rows >> 2)))) { | 1936 | 3 * vc->vc_rows >> 2)))) { |
1937 | if (vc->vc_rows - b > 0) | 1937 | if (vc->vc_rows - b > 0) |
1938 | fbcon_bmove(vc, b, 0, b - count, 0, | 1938 | fbcon_bmove(vc, b, 0, b - count, 0, |
1939 | vc->vc_rows - b, | 1939 | vc->vc_rows - b, |
1940 | vc->vc_cols); | 1940 | vc->vc_cols); |
1941 | ypan_down(vc, count); | 1941 | ypan_down(vc, count); |
1942 | if (t > 0) | 1942 | if (t > 0) |
1943 | fbcon_bmove(vc, count, 0, 0, 0, t, | 1943 | fbcon_bmove(vc, count, 0, 0, 0, t, |
1944 | vc->vc_cols); | 1944 | vc->vc_cols); |
1945 | } else if (info->flags & FBINFO_READS_FAST) | 1945 | } else if (info->flags & FBINFO_READS_FAST) |
1946 | fbcon_bmove(vc, t, 0, t + count, 0, | 1946 | fbcon_bmove(vc, t, 0, t + count, 0, |
1947 | b - t - count, vc->vc_cols); | 1947 | b - t - count, vc->vc_cols); |
1948 | else | 1948 | else |
1949 | goto redraw_down; | 1949 | goto redraw_down; |
1950 | fbcon_clear(vc, t, 0, count, vc->vc_cols); | 1950 | fbcon_clear(vc, t, 0, count, vc->vc_cols); |
1951 | break; | 1951 | break; |
1952 | 1952 | ||
1953 | case SCROLL_PAN_REDRAW: | 1953 | case SCROLL_PAN_REDRAW: |
1954 | if ((count - p->yscroll <= p->vrows - vc->vc_rows) | 1954 | if ((count - p->yscroll <= p->vrows - vc->vc_rows) |
1955 | && ((!scroll_partial && (b - t == vc->vc_rows)) | 1955 | && ((!scroll_partial && (b - t == vc->vc_rows)) |
1956 | || (scroll_partial | 1956 | || (scroll_partial |
1957 | && (b - t - count > | 1957 | && (b - t - count > |
1958 | 3 * vc->vc_rows >> 2)))) { | 1958 | 3 * vc->vc_rows >> 2)))) { |
1959 | if (vc->vc_rows - b > 0) | 1959 | if (vc->vc_rows - b > 0) |
1960 | fbcon_redraw_move(vc, p, b, vc->vc_rows - b, | 1960 | fbcon_redraw_move(vc, p, b, vc->vc_rows - b, |
1961 | b - count); | 1961 | b - count); |
1962 | ypan_down_redraw(vc, t, count); | 1962 | ypan_down_redraw(vc, t, count); |
1963 | if (t > 0) | 1963 | if (t > 0) |
1964 | fbcon_redraw_move(vc, p, count, t, 0); | 1964 | fbcon_redraw_move(vc, p, count, t, 0); |
1965 | } else | 1965 | } else |
1966 | fbcon_redraw_move(vc, p, t, b - t - count, t + count); | 1966 | fbcon_redraw_move(vc, p, t, b - t - count, t + count); |
1967 | fbcon_clear(vc, t, 0, count, vc->vc_cols); | 1967 | fbcon_clear(vc, t, 0, count, vc->vc_cols); |
1968 | break; | 1968 | break; |
1969 | 1969 | ||
1970 | case SCROLL_REDRAW: | 1970 | case SCROLL_REDRAW: |
1971 | redraw_down: | 1971 | redraw_down: |
1972 | fbcon_redraw(vc, p, b - 1, b - t - count, | 1972 | fbcon_redraw(vc, p, b - 1, b - t - count, |
1973 | -count * vc->vc_cols); | 1973 | -count * vc->vc_cols); |
1974 | fbcon_clear(vc, t, 0, count, vc->vc_cols); | 1974 | fbcon_clear(vc, t, 0, count, vc->vc_cols); |
1975 | scr_memsetw((unsigned short *) (vc->vc_origin + | 1975 | scr_memsetw((unsigned short *) (vc->vc_origin + |
1976 | vc->vc_size_row * | 1976 | vc->vc_size_row * |
1977 | t), | 1977 | t), |
1978 | vc->vc_video_erase_char, | 1978 | vc->vc_video_erase_char, |
1979 | vc->vc_size_row * count); | 1979 | vc->vc_size_row * count); |
1980 | return 1; | 1980 | return 1; |
1981 | } | 1981 | } |
1982 | } | 1982 | } |
1983 | return 0; | 1983 | return 0; |
1984 | } | 1984 | } |
1985 | 1985 | ||
1986 | 1986 | ||
1987 | static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, | 1987 | static void fbcon_bmove(struct vc_data *vc, int sy, int sx, int dy, int dx, |
1988 | int height, int width) | 1988 | int height, int width) |
1989 | { | 1989 | { |
1990 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 1990 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
1991 | struct display *p = &fb_display[vc->vc_num]; | 1991 | struct display *p = &fb_display[vc->vc_num]; |
1992 | 1992 | ||
1993 | if (fbcon_is_inactive(vc, info)) | 1993 | if (fbcon_is_inactive(vc, info)) |
1994 | return; | 1994 | return; |
1995 | 1995 | ||
1996 | if (!width || !height) | 1996 | if (!width || !height) |
1997 | return; | 1997 | return; |
1998 | 1998 | ||
1999 | /* Split blits that cross physical y_wrap case. | 1999 | /* Split blits that cross physical y_wrap case. |
2000 | * Pathological case involves 4 blits, better to use recursive | 2000 | * Pathological case involves 4 blits, better to use recursive |
2001 | * code rather than unrolled case | 2001 | * code rather than unrolled case |
2002 | * | 2002 | * |
2003 | * Recursive invocations don't need to erase the cursor over and | 2003 | * Recursive invocations don't need to erase the cursor over and |
2004 | * over again, so we use fbcon_bmove_rec() | 2004 | * over again, so we use fbcon_bmove_rec() |
2005 | */ | 2005 | */ |
2006 | fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width, | 2006 | fbcon_bmove_rec(vc, p, sy, sx, dy, dx, height, width, |
2007 | p->vrows - p->yscroll); | 2007 | p->vrows - p->yscroll); |
2008 | } | 2008 | } |
2009 | 2009 | ||
2010 | static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx, | 2010 | static void fbcon_bmove_rec(struct vc_data *vc, struct display *p, int sy, int sx, |
2011 | int dy, int dx, int height, int width, u_int y_break) | 2011 | int dy, int dx, int height, int width, u_int y_break) |
2012 | { | 2012 | { |
2013 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 2013 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
2014 | struct fbcon_ops *ops = info->fbcon_par; | 2014 | struct fbcon_ops *ops = info->fbcon_par; |
2015 | u_int b; | 2015 | u_int b; |
2016 | 2016 | ||
2017 | if (sy < y_break && sy + height > y_break) { | 2017 | if (sy < y_break && sy + height > y_break) { |
2018 | b = y_break - sy; | 2018 | b = y_break - sy; |
2019 | if (dy < sy) { /* Avoid trashing self */ | 2019 | if (dy < sy) { /* Avoid trashing self */ |
2020 | fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, | 2020 | fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, |
2021 | y_break); | 2021 | y_break); |
2022 | fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, | 2022 | fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, |
2023 | height - b, width, y_break); | 2023 | height - b, width, y_break); |
2024 | } else { | 2024 | } else { |
2025 | fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, | 2025 | fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, |
2026 | height - b, width, y_break); | 2026 | height - b, width, y_break); |
2027 | fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, | 2027 | fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, |
2028 | y_break); | 2028 | y_break); |
2029 | } | 2029 | } |
2030 | return; | 2030 | return; |
2031 | } | 2031 | } |
2032 | 2032 | ||
2033 | if (dy < y_break && dy + height > y_break) { | 2033 | if (dy < y_break && dy + height > y_break) { |
2034 | b = y_break - dy; | 2034 | b = y_break - dy; |
2035 | if (dy < sy) { /* Avoid trashing self */ | 2035 | if (dy < sy) { /* Avoid trashing self */ |
2036 | fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, | 2036 | fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, |
2037 | y_break); | 2037 | y_break); |
2038 | fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, | 2038 | fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, |
2039 | height - b, width, y_break); | 2039 | height - b, width, y_break); |
2040 | } else { | 2040 | } else { |
2041 | fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, | 2041 | fbcon_bmove_rec(vc, p, sy + b, sx, dy + b, dx, |
2042 | height - b, width, y_break); | 2042 | height - b, width, y_break); |
2043 | fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, | 2043 | fbcon_bmove_rec(vc, p, sy, sx, dy, dx, b, width, |
2044 | y_break); | 2044 | y_break); |
2045 | } | 2045 | } |
2046 | return; | 2046 | return; |
2047 | } | 2047 | } |
2048 | ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, | 2048 | ops->bmove(vc, info, real_y(p, sy), sx, real_y(p, dy), dx, |
2049 | height, width); | 2049 | height, width); |
2050 | } | 2050 | } |
2051 | 2051 | ||
2052 | static void updatescrollmode(struct display *p, | 2052 | static void updatescrollmode(struct display *p, |
2053 | struct fb_info *info, | 2053 | struct fb_info *info, |
2054 | struct vc_data *vc) | 2054 | struct vc_data *vc) |
2055 | { | 2055 | { |
2056 | struct fbcon_ops *ops = info->fbcon_par; | 2056 | struct fbcon_ops *ops = info->fbcon_par; |
2057 | int fh = vc->vc_font.height; | 2057 | int fh = vc->vc_font.height; |
2058 | int cap = info->flags; | 2058 | int cap = info->flags; |
2059 | u16 t = 0; | 2059 | u16 t = 0; |
2060 | int ypan = FBCON_SWAP(ops->rotate, info->fix.ypanstep, | 2060 | int ypan = FBCON_SWAP(ops->rotate, info->fix.ypanstep, |
2061 | info->fix.xpanstep); | 2061 | info->fix.xpanstep); |
2062 | int ywrap = FBCON_SWAP(ops->rotate, info->fix.ywrapstep, t); | 2062 | int ywrap = FBCON_SWAP(ops->rotate, info->fix.ywrapstep, t); |
2063 | int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); | 2063 | int yres = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); |
2064 | int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual, | 2064 | int vyres = FBCON_SWAP(ops->rotate, info->var.yres_virtual, |
2065 | info->var.xres_virtual); | 2065 | info->var.xres_virtual); |
2066 | int good_pan = (cap & FBINFO_HWACCEL_YPAN) && | 2066 | int good_pan = (cap & FBINFO_HWACCEL_YPAN) && |
2067 | divides(ypan, vc->vc_font.height) && vyres > yres; | 2067 | divides(ypan, vc->vc_font.height) && vyres > yres; |
2068 | int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) && | 2068 | int good_wrap = (cap & FBINFO_HWACCEL_YWRAP) && |
2069 | divides(ywrap, vc->vc_font.height) && | 2069 | divides(ywrap, vc->vc_font.height) && |
2070 | divides(vc->vc_font.height, vyres) && | 2070 | divides(vc->vc_font.height, vyres) && |
2071 | divides(vc->vc_font.height, yres); | 2071 | divides(vc->vc_font.height, yres); |
2072 | int reading_fast = cap & FBINFO_READS_FAST; | 2072 | int reading_fast = cap & FBINFO_READS_FAST; |
2073 | int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) && | 2073 | int fast_copyarea = (cap & FBINFO_HWACCEL_COPYAREA) && |
2074 | !(cap & FBINFO_HWACCEL_DISABLED); | 2074 | !(cap & FBINFO_HWACCEL_DISABLED); |
2075 | int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) && | 2075 | int fast_imageblit = (cap & FBINFO_HWACCEL_IMAGEBLIT) && |
2076 | !(cap & FBINFO_HWACCEL_DISABLED); | 2076 | !(cap & FBINFO_HWACCEL_DISABLED); |
2077 | 2077 | ||
2078 | p->vrows = vyres/fh; | 2078 | p->vrows = vyres/fh; |
2079 | if (yres > (fh * (vc->vc_rows + 1))) | 2079 | if (yres > (fh * (vc->vc_rows + 1))) |
2080 | p->vrows -= (yres - (fh * vc->vc_rows)) / fh; | 2080 | p->vrows -= (yres - (fh * vc->vc_rows)) / fh; |
2081 | if ((yres % fh) && (vyres % fh < yres % fh)) | 2081 | if ((yres % fh) && (vyres % fh < yres % fh)) |
2082 | p->vrows--; | 2082 | p->vrows--; |
2083 | 2083 | ||
2084 | if (good_wrap || good_pan) { | 2084 | if (good_wrap || good_pan) { |
2085 | if (reading_fast || fast_copyarea) | 2085 | if (reading_fast || fast_copyarea) |
2086 | p->scrollmode = good_wrap ? | 2086 | p->scrollmode = good_wrap ? |
2087 | SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE; | 2087 | SCROLL_WRAP_MOVE : SCROLL_PAN_MOVE; |
2088 | else | 2088 | else |
2089 | p->scrollmode = good_wrap ? SCROLL_REDRAW : | 2089 | p->scrollmode = good_wrap ? SCROLL_REDRAW : |
2090 | SCROLL_PAN_REDRAW; | 2090 | SCROLL_PAN_REDRAW; |
2091 | } else { | 2091 | } else { |
2092 | if (reading_fast || (fast_copyarea && !fast_imageblit)) | 2092 | if (reading_fast || (fast_copyarea && !fast_imageblit)) |
2093 | p->scrollmode = SCROLL_MOVE; | 2093 | p->scrollmode = SCROLL_MOVE; |
2094 | else | 2094 | else |
2095 | p->scrollmode = SCROLL_REDRAW; | 2095 | p->scrollmode = SCROLL_REDRAW; |
2096 | } | 2096 | } |
2097 | } | 2097 | } |
2098 | 2098 | ||
2099 | static int fbcon_resize(struct vc_data *vc, unsigned int width, | 2099 | static int fbcon_resize(struct vc_data *vc, unsigned int width, |
2100 | unsigned int height, unsigned int user) | 2100 | unsigned int height, unsigned int user) |
2101 | { | 2101 | { |
2102 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 2102 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
2103 | struct fbcon_ops *ops = info->fbcon_par; | 2103 | struct fbcon_ops *ops = info->fbcon_par; |
2104 | struct display *p = &fb_display[vc->vc_num]; | 2104 | struct display *p = &fb_display[vc->vc_num]; |
2105 | struct fb_var_screeninfo var = info->var; | 2105 | struct fb_var_screeninfo var = info->var; |
2106 | int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh; | 2106 | int x_diff, y_diff, virt_w, virt_h, virt_fw, virt_fh; |
2107 | 2107 | ||
2108 | virt_w = FBCON_SWAP(ops->rotate, width, height); | 2108 | virt_w = FBCON_SWAP(ops->rotate, width, height); |
2109 | virt_h = FBCON_SWAP(ops->rotate, height, width); | 2109 | virt_h = FBCON_SWAP(ops->rotate, height, width); |
2110 | virt_fw = FBCON_SWAP(ops->rotate, vc->vc_font.width, | 2110 | virt_fw = FBCON_SWAP(ops->rotate, vc->vc_font.width, |
2111 | vc->vc_font.height); | 2111 | vc->vc_font.height); |
2112 | virt_fh = FBCON_SWAP(ops->rotate, vc->vc_font.height, | 2112 | virt_fh = FBCON_SWAP(ops->rotate, vc->vc_font.height, |
2113 | vc->vc_font.width); | 2113 | vc->vc_font.width); |
2114 | var.xres = virt_w * virt_fw; | 2114 | var.xres = virt_w * virt_fw; |
2115 | var.yres = virt_h * virt_fh; | 2115 | var.yres = virt_h * virt_fh; |
2116 | x_diff = info->var.xres - var.xres; | 2116 | x_diff = info->var.xres - var.xres; |
2117 | y_diff = info->var.yres - var.yres; | 2117 | y_diff = info->var.yres - var.yres; |
2118 | if (x_diff < 0 || x_diff > virt_fw || | 2118 | if (x_diff < 0 || x_diff > virt_fw || |
2119 | y_diff < 0 || y_diff > virt_fh) { | 2119 | y_diff < 0 || y_diff > virt_fh) { |
2120 | const struct fb_videomode *mode; | 2120 | const struct fb_videomode *mode; |
2121 | 2121 | ||
2122 | DPRINTK("attempting resize %ix%i\n", var.xres, var.yres); | 2122 | DPRINTK("attempting resize %ix%i\n", var.xres, var.yres); |
2123 | mode = fb_find_best_mode(&var, &info->modelist); | 2123 | mode = fb_find_best_mode(&var, &info->modelist); |
2124 | if (mode == NULL) | 2124 | if (mode == NULL) |
2125 | return -EINVAL; | 2125 | return -EINVAL; |
2126 | display_to_var(&var, p); | 2126 | display_to_var(&var, p); |
2127 | fb_videomode_to_var(&var, mode); | 2127 | fb_videomode_to_var(&var, mode); |
2128 | 2128 | ||
2129 | if (virt_w > var.xres/virt_fw || virt_h > var.yres/virt_fh) | 2129 | if (virt_w > var.xres/virt_fw || virt_h > var.yres/virt_fh) |
2130 | return -EINVAL; | 2130 | return -EINVAL; |
2131 | 2131 | ||
2132 | DPRINTK("resize now %ix%i\n", var.xres, var.yres); | 2132 | DPRINTK("resize now %ix%i\n", var.xres, var.yres); |
2133 | if (CON_IS_VISIBLE(vc)) { | 2133 | if (CON_IS_VISIBLE(vc)) { |
2134 | var.activate = FB_ACTIVATE_NOW | | 2134 | var.activate = FB_ACTIVATE_NOW | |
2135 | FB_ACTIVATE_FORCE; | 2135 | FB_ACTIVATE_FORCE; |
2136 | fb_set_var(info, &var); | 2136 | fb_set_var(info, &var); |
2137 | } | 2137 | } |
2138 | var_to_display(p, &info->var, info); | 2138 | var_to_display(p, &info->var, info); |
2139 | ops->var = info->var; | 2139 | ops->var = info->var; |
2140 | } | 2140 | } |
2141 | updatescrollmode(p, info, vc); | 2141 | updatescrollmode(p, info, vc); |
2142 | return 0; | 2142 | return 0; |
2143 | } | 2143 | } |
2144 | 2144 | ||
2145 | static int fbcon_switch(struct vc_data *vc) | 2145 | static int fbcon_switch(struct vc_data *vc) |
2146 | { | 2146 | { |
2147 | struct fb_info *info, *old_info = NULL; | 2147 | struct fb_info *info, *old_info = NULL; |
2148 | struct fbcon_ops *ops; | 2148 | struct fbcon_ops *ops; |
2149 | struct display *p = &fb_display[vc->vc_num]; | 2149 | struct display *p = &fb_display[vc->vc_num]; |
2150 | struct fb_var_screeninfo var; | 2150 | struct fb_var_screeninfo var; |
2151 | int i, ret, prev_console, charcnt = 256; | 2151 | int i, ret, prev_console, charcnt = 256; |
2152 | 2152 | ||
2153 | info = registered_fb[con2fb_map[vc->vc_num]]; | 2153 | info = registered_fb[con2fb_map[vc->vc_num]]; |
2154 | ops = info->fbcon_par; | 2154 | ops = info->fbcon_par; |
2155 | 2155 | ||
2156 | if (softback_top) { | 2156 | if (softback_top) { |
2157 | if (softback_lines) | 2157 | if (softback_lines) |
2158 | fbcon_set_origin(vc); | 2158 | fbcon_set_origin(vc); |
2159 | softback_top = softback_curr = softback_in = softback_buf; | 2159 | softback_top = softback_curr = softback_in = softback_buf; |
2160 | softback_lines = 0; | 2160 | softback_lines = 0; |
2161 | fbcon_update_softback(vc); | 2161 | fbcon_update_softback(vc); |
2162 | } | 2162 | } |
2163 | 2163 | ||
2164 | if (logo_shown >= 0) { | 2164 | if (logo_shown >= 0) { |
2165 | struct vc_data *conp2 = vc_cons[logo_shown].d; | 2165 | struct vc_data *conp2 = vc_cons[logo_shown].d; |
2166 | 2166 | ||
2167 | if (conp2->vc_top == logo_lines | 2167 | if (conp2->vc_top == logo_lines |
2168 | && conp2->vc_bottom == conp2->vc_rows) | 2168 | && conp2->vc_bottom == conp2->vc_rows) |
2169 | conp2->vc_top = 0; | 2169 | conp2->vc_top = 0; |
2170 | logo_shown = FBCON_LOGO_CANSHOW; | 2170 | logo_shown = FBCON_LOGO_CANSHOW; |
2171 | } | 2171 | } |
2172 | 2172 | ||
2173 | prev_console = ops->currcon; | 2173 | prev_console = ops->currcon; |
2174 | if (prev_console != -1) | 2174 | if (prev_console != -1) |
2175 | old_info = registered_fb[con2fb_map[prev_console]]; | 2175 | old_info = registered_fb[con2fb_map[prev_console]]; |
2176 | /* | 2176 | /* |
2177 | * FIXME: If we have multiple fbdev's loaded, we need to | 2177 | * FIXME: If we have multiple fbdev's loaded, we need to |
2178 | * update all info->currcon. Perhaps, we can place this | 2178 | * update all info->currcon. Perhaps, we can place this |
2179 | * in a centralized structure, but this might break some | 2179 | * in a centralized structure, but this might break some |
2180 | * drivers. | 2180 | * drivers. |
2181 | * | 2181 | * |
2182 | * info->currcon = vc->vc_num; | 2182 | * info->currcon = vc->vc_num; |
2183 | */ | 2183 | */ |
2184 | for (i = 0; i < FB_MAX; i++) { | 2184 | for (i = 0; i < FB_MAX; i++) { |
2185 | if (registered_fb[i] != NULL && registered_fb[i]->fbcon_par) { | 2185 | if (registered_fb[i] != NULL && registered_fb[i]->fbcon_par) { |
2186 | struct fbcon_ops *o = registered_fb[i]->fbcon_par; | 2186 | struct fbcon_ops *o = registered_fb[i]->fbcon_par; |
2187 | 2187 | ||
2188 | o->currcon = vc->vc_num; | 2188 | o->currcon = vc->vc_num; |
2189 | } | 2189 | } |
2190 | } | 2190 | } |
2191 | memset(&var, 0, sizeof(struct fb_var_screeninfo)); | 2191 | memset(&var, 0, sizeof(struct fb_var_screeninfo)); |
2192 | display_to_var(&var, p); | 2192 | display_to_var(&var, p); |
2193 | var.activate = FB_ACTIVATE_NOW; | 2193 | var.activate = FB_ACTIVATE_NOW; |
2194 | 2194 | ||
2195 | /* | 2195 | /* |
2196 | * make sure we don't unnecessarily trip the memcmp() | 2196 | * make sure we don't unnecessarily trip the memcmp() |
2197 | * in fb_set_var() | 2197 | * in fb_set_var() |
2198 | */ | 2198 | */ |
2199 | info->var.activate = var.activate; | 2199 | info->var.activate = var.activate; |
2200 | var.vmode |= info->var.vmode & ~FB_VMODE_MASK; | 2200 | var.vmode |= info->var.vmode & ~FB_VMODE_MASK; |
2201 | fb_set_var(info, &var); | 2201 | fb_set_var(info, &var); |
2202 | ops->var = info->var; | 2202 | ops->var = info->var; |
2203 | 2203 | ||
2204 | if (old_info != NULL && (old_info != info || | 2204 | if (old_info != NULL && (old_info != info || |
2205 | info->flags & FBINFO_MISC_ALWAYS_SETPAR)) { | 2205 | info->flags & FBINFO_MISC_ALWAYS_SETPAR)) { |
2206 | if (info->fbops->fb_set_par) { | 2206 | if (info->fbops->fb_set_par) { |
2207 | ret = info->fbops->fb_set_par(info); | 2207 | ret = info->fbops->fb_set_par(info); |
2208 | 2208 | ||
2209 | if (ret) | 2209 | if (ret) |
2210 | printk(KERN_ERR "fbcon_switch: detected " | 2210 | printk(KERN_ERR "fbcon_switch: detected " |
2211 | "unhandled fb_set_par error, " | 2211 | "unhandled fb_set_par error, " |
2212 | "error code %d\n", ret); | 2212 | "error code %d\n", ret); |
2213 | } | 2213 | } |
2214 | 2214 | ||
2215 | if (old_info != info) | 2215 | if (old_info != info) |
2216 | fbcon_del_cursor_timer(old_info); | 2216 | fbcon_del_cursor_timer(old_info); |
2217 | } | 2217 | } |
2218 | 2218 | ||
2219 | if (fbcon_is_inactive(vc, info) || | 2219 | if (fbcon_is_inactive(vc, info) || |
2220 | ops->blank_state != FB_BLANK_UNBLANK) | 2220 | ops->blank_state != FB_BLANK_UNBLANK) |
2221 | fbcon_del_cursor_timer(info); | 2221 | fbcon_del_cursor_timer(info); |
2222 | else | 2222 | else |
2223 | fbcon_add_cursor_timer(info); | 2223 | fbcon_add_cursor_timer(info); |
2224 | 2224 | ||
2225 | set_blitting_type(vc, info); | 2225 | set_blitting_type(vc, info); |
2226 | ops->cursor_reset = 1; | 2226 | ops->cursor_reset = 1; |
2227 | 2227 | ||
2228 | if (ops->rotate_font && ops->rotate_font(info, vc)) { | 2228 | if (ops->rotate_font && ops->rotate_font(info, vc)) { |
2229 | ops->rotate = FB_ROTATE_UR; | 2229 | ops->rotate = FB_ROTATE_UR; |
2230 | set_blitting_type(vc, info); | 2230 | set_blitting_type(vc, info); |
2231 | } | 2231 | } |
2232 | 2232 | ||
2233 | vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); | 2233 | vc->vc_can_do_color = (fb_get_color_depth(&info->var, &info->fix)!=1); |
2234 | vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; | 2234 | vc->vc_complement_mask = vc->vc_can_do_color ? 0x7700 : 0x0800; |
2235 | 2235 | ||
2236 | if (p->userfont) | 2236 | if (p->userfont) |
2237 | charcnt = FNTCHARCNT(vc->vc_font.data); | 2237 | charcnt = FNTCHARCNT(vc->vc_font.data); |
2238 | 2238 | ||
2239 | if (charcnt > 256) | 2239 | if (charcnt > 256) |
2240 | vc->vc_complement_mask <<= 1; | 2240 | vc->vc_complement_mask <<= 1; |
2241 | 2241 | ||
2242 | updatescrollmode(p, info, vc); | 2242 | updatescrollmode(p, info, vc); |
2243 | 2243 | ||
2244 | switch (p->scrollmode) { | 2244 | switch (p->scrollmode) { |
2245 | case SCROLL_WRAP_MOVE: | 2245 | case SCROLL_WRAP_MOVE: |
2246 | scrollback_phys_max = p->vrows - vc->vc_rows; | 2246 | scrollback_phys_max = p->vrows - vc->vc_rows; |
2247 | break; | 2247 | break; |
2248 | case SCROLL_PAN_MOVE: | 2248 | case SCROLL_PAN_MOVE: |
2249 | case SCROLL_PAN_REDRAW: | 2249 | case SCROLL_PAN_REDRAW: |
2250 | scrollback_phys_max = p->vrows - 2 * vc->vc_rows; | 2250 | scrollback_phys_max = p->vrows - 2 * vc->vc_rows; |
2251 | if (scrollback_phys_max < 0) | 2251 | if (scrollback_phys_max < 0) |
2252 | scrollback_phys_max = 0; | 2252 | scrollback_phys_max = 0; |
2253 | break; | 2253 | break; |
2254 | default: | 2254 | default: |
2255 | scrollback_phys_max = 0; | 2255 | scrollback_phys_max = 0; |
2256 | break; | 2256 | break; |
2257 | } | 2257 | } |
2258 | 2258 | ||
2259 | scrollback_max = 0; | 2259 | scrollback_max = 0; |
2260 | scrollback_current = 0; | 2260 | scrollback_current = 0; |
2261 | 2261 | ||
2262 | if (!fbcon_is_inactive(vc, info)) { | 2262 | if (!fbcon_is_inactive(vc, info)) { |
2263 | ops->var.xoffset = ops->var.yoffset = p->yscroll = 0; | 2263 | ops->var.xoffset = ops->var.yoffset = p->yscroll = 0; |
2264 | ops->update_start(info); | 2264 | ops->update_start(info); |
2265 | } | 2265 | } |
2266 | 2266 | ||
2267 | fbcon_set_palette(vc, color_table); | 2267 | fbcon_set_palette(vc, color_table); |
2268 | fbcon_clear_margins(vc, 0); | 2268 | fbcon_clear_margins(vc, 0); |
2269 | 2269 | ||
2270 | if (logo_shown == FBCON_LOGO_DRAW) { | 2270 | if (logo_shown == FBCON_LOGO_DRAW) { |
2271 | 2271 | ||
2272 | logo_shown = fg_console; | 2272 | logo_shown = fg_console; |
2273 | /* This is protected above by initmem_freed */ | 2273 | /* This is protected above by initmem_freed */ |
2274 | fb_show_logo(info, ops->rotate); | 2274 | fb_show_logo(info, ops->rotate); |
2275 | update_region(vc, | 2275 | update_region(vc, |
2276 | vc->vc_origin + vc->vc_size_row * vc->vc_top, | 2276 | vc->vc_origin + vc->vc_size_row * vc->vc_top, |
2277 | vc->vc_size_row * (vc->vc_bottom - | 2277 | vc->vc_size_row * (vc->vc_bottom - |
2278 | vc->vc_top) / 2); | 2278 | vc->vc_top) / 2); |
2279 | return 0; | 2279 | return 0; |
2280 | } | 2280 | } |
2281 | return 1; | 2281 | return 1; |
2282 | } | 2282 | } |
2283 | 2283 | ||
2284 | static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info, | 2284 | static void fbcon_generic_blank(struct vc_data *vc, struct fb_info *info, |
2285 | int blank) | 2285 | int blank) |
2286 | { | 2286 | { |
2287 | struct fb_event event; | 2287 | struct fb_event event; |
2288 | 2288 | ||
2289 | if (blank) { | 2289 | if (blank) { |
2290 | unsigned short charmask = vc->vc_hi_font_mask ? | 2290 | unsigned short charmask = vc->vc_hi_font_mask ? |
2291 | 0x1ff : 0xff; | 2291 | 0x1ff : 0xff; |
2292 | unsigned short oldc; | 2292 | unsigned short oldc; |
2293 | 2293 | ||
2294 | oldc = vc->vc_video_erase_char; | 2294 | oldc = vc->vc_video_erase_char; |
2295 | vc->vc_video_erase_char &= charmask; | 2295 | vc->vc_video_erase_char &= charmask; |
2296 | fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols); | 2296 | fbcon_clear(vc, 0, 0, vc->vc_rows, vc->vc_cols); |
2297 | vc->vc_video_erase_char = oldc; | 2297 | vc->vc_video_erase_char = oldc; |
2298 | } | 2298 | } |
2299 | 2299 | ||
2300 | 2300 | ||
2301 | if (!lock_fb_info(info)) | 2301 | if (!lock_fb_info(info)) |
2302 | return; | 2302 | return; |
2303 | event.info = info; | 2303 | event.info = info; |
2304 | event.data = ␣ | 2304 | event.data = ␣ |
2305 | fb_notifier_call_chain(FB_EVENT_CONBLANK, &event); | 2305 | fb_notifier_call_chain(FB_EVENT_CONBLANK, &event); |
2306 | unlock_fb_info(info); | 2306 | unlock_fb_info(info); |
2307 | } | 2307 | } |
2308 | 2308 | ||
2309 | static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) | 2309 | static int fbcon_blank(struct vc_data *vc, int blank, int mode_switch) |
2310 | { | 2310 | { |
2311 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 2311 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
2312 | struct fbcon_ops *ops = info->fbcon_par; | 2312 | struct fbcon_ops *ops = info->fbcon_par; |
2313 | 2313 | ||
2314 | if (mode_switch) { | 2314 | if (mode_switch) { |
2315 | struct fb_var_screeninfo var = info->var; | 2315 | struct fb_var_screeninfo var = info->var; |
2316 | 2316 | ||
2317 | ops->graphics = 1; | 2317 | ops->graphics = 1; |
2318 | 2318 | ||
2319 | if (!blank) { | 2319 | if (!blank) { |
2320 | var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; | 2320 | var.activate = FB_ACTIVATE_NOW | FB_ACTIVATE_FORCE; |
2321 | fb_set_var(info, &var); | 2321 | fb_set_var(info, &var); |
2322 | ops->graphics = 0; | 2322 | ops->graphics = 0; |
2323 | ops->var = info->var; | 2323 | ops->var = info->var; |
2324 | } | 2324 | } |
2325 | } | 2325 | } |
2326 | 2326 | ||
2327 | if (!fbcon_is_inactive(vc, info)) { | 2327 | if (!fbcon_is_inactive(vc, info)) { |
2328 | if (ops->blank_state != blank) { | 2328 | if (ops->blank_state != blank) { |
2329 | ops->blank_state = blank; | 2329 | ops->blank_state = blank; |
2330 | fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW); | 2330 | fbcon_cursor(vc, blank ? CM_ERASE : CM_DRAW); |
2331 | ops->cursor_flash = (!blank); | 2331 | ops->cursor_flash = (!blank); |
2332 | 2332 | ||
2333 | if (!(info->flags & FBINFO_MISC_USEREVENT)) | 2333 | if (!(info->flags & FBINFO_MISC_USEREVENT)) |
2334 | if (fb_blank(info, blank)) | 2334 | if (fb_blank(info, blank)) |
2335 | fbcon_generic_blank(vc, info, blank); | 2335 | fbcon_generic_blank(vc, info, blank); |
2336 | } | 2336 | } |
2337 | 2337 | ||
2338 | if (!blank) | 2338 | if (!blank) |
2339 | update_screen(vc); | 2339 | update_screen(vc); |
2340 | } | 2340 | } |
2341 | 2341 | ||
2342 | if (mode_switch || fbcon_is_inactive(vc, info) || | 2342 | if (mode_switch || fbcon_is_inactive(vc, info) || |
2343 | ops->blank_state != FB_BLANK_UNBLANK) | 2343 | ops->blank_state != FB_BLANK_UNBLANK) |
2344 | fbcon_del_cursor_timer(info); | 2344 | fbcon_del_cursor_timer(info); |
2345 | else | 2345 | else |
2346 | fbcon_add_cursor_timer(info); | 2346 | fbcon_add_cursor_timer(info); |
2347 | 2347 | ||
2348 | return 0; | 2348 | return 0; |
2349 | } | 2349 | } |
2350 | 2350 | ||
2351 | static int fbcon_debug_enter(struct vc_data *vc) | 2351 | static int fbcon_debug_enter(struct vc_data *vc) |
2352 | { | 2352 | { |
2353 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 2353 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
2354 | struct fbcon_ops *ops = info->fbcon_par; | 2354 | struct fbcon_ops *ops = info->fbcon_par; |
2355 | 2355 | ||
2356 | ops->save_graphics = ops->graphics; | 2356 | ops->save_graphics = ops->graphics; |
2357 | ops->graphics = 0; | 2357 | ops->graphics = 0; |
2358 | if (info->fbops->fb_debug_enter) | 2358 | if (info->fbops->fb_debug_enter) |
2359 | info->fbops->fb_debug_enter(info); | 2359 | info->fbops->fb_debug_enter(info); |
2360 | fbcon_set_palette(vc, color_table); | 2360 | fbcon_set_palette(vc, color_table); |
2361 | return 0; | 2361 | return 0; |
2362 | } | 2362 | } |
2363 | 2363 | ||
2364 | static int fbcon_debug_leave(struct vc_data *vc) | 2364 | static int fbcon_debug_leave(struct vc_data *vc) |
2365 | { | 2365 | { |
2366 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 2366 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
2367 | struct fbcon_ops *ops = info->fbcon_par; | 2367 | struct fbcon_ops *ops = info->fbcon_par; |
2368 | 2368 | ||
2369 | ops->graphics = ops->save_graphics; | 2369 | ops->graphics = ops->save_graphics; |
2370 | if (info->fbops->fb_debug_leave) | 2370 | if (info->fbops->fb_debug_leave) |
2371 | info->fbops->fb_debug_leave(info); | 2371 | info->fbops->fb_debug_leave(info); |
2372 | return 0; | 2372 | return 0; |
2373 | } | 2373 | } |
2374 | 2374 | ||
2375 | static int fbcon_get_font(struct vc_data *vc, struct console_font *font) | 2375 | static int fbcon_get_font(struct vc_data *vc, struct console_font *font) |
2376 | { | 2376 | { |
2377 | u8 *fontdata = vc->vc_font.data; | 2377 | u8 *fontdata = vc->vc_font.data; |
2378 | u8 *data = font->data; | 2378 | u8 *data = font->data; |
2379 | int i, j; | 2379 | int i, j; |
2380 | 2380 | ||
2381 | font->width = vc->vc_font.width; | 2381 | font->width = vc->vc_font.width; |
2382 | font->height = vc->vc_font.height; | 2382 | font->height = vc->vc_font.height; |
2383 | font->charcount = vc->vc_hi_font_mask ? 512 : 256; | 2383 | font->charcount = vc->vc_hi_font_mask ? 512 : 256; |
2384 | if (!font->data) | 2384 | if (!font->data) |
2385 | return 0; | 2385 | return 0; |
2386 | 2386 | ||
2387 | if (font->width <= 8) { | 2387 | if (font->width <= 8) { |
2388 | j = vc->vc_font.height; | 2388 | j = vc->vc_font.height; |
2389 | for (i = 0; i < font->charcount; i++) { | 2389 | for (i = 0; i < font->charcount; i++) { |
2390 | memcpy(data, fontdata, j); | 2390 | memcpy(data, fontdata, j); |
2391 | memset(data + j, 0, 32 - j); | 2391 | memset(data + j, 0, 32 - j); |
2392 | data += 32; | 2392 | data += 32; |
2393 | fontdata += j; | 2393 | fontdata += j; |
2394 | } | 2394 | } |
2395 | } else if (font->width <= 16) { | 2395 | } else if (font->width <= 16) { |
2396 | j = vc->vc_font.height * 2; | 2396 | j = vc->vc_font.height * 2; |
2397 | for (i = 0; i < font->charcount; i++) { | 2397 | for (i = 0; i < font->charcount; i++) { |
2398 | memcpy(data, fontdata, j); | 2398 | memcpy(data, fontdata, j); |
2399 | memset(data + j, 0, 64 - j); | 2399 | memset(data + j, 0, 64 - j); |
2400 | data += 64; | 2400 | data += 64; |
2401 | fontdata += j; | 2401 | fontdata += j; |
2402 | } | 2402 | } |
2403 | } else if (font->width <= 24) { | 2403 | } else if (font->width <= 24) { |
2404 | for (i = 0; i < font->charcount; i++) { | 2404 | for (i = 0; i < font->charcount; i++) { |
2405 | for (j = 0; j < vc->vc_font.height; j++) { | 2405 | for (j = 0; j < vc->vc_font.height; j++) { |
2406 | *data++ = fontdata[0]; | 2406 | *data++ = fontdata[0]; |
2407 | *data++ = fontdata[1]; | 2407 | *data++ = fontdata[1]; |
2408 | *data++ = fontdata[2]; | 2408 | *data++ = fontdata[2]; |
2409 | fontdata += sizeof(u32); | 2409 | fontdata += sizeof(u32); |
2410 | } | 2410 | } |
2411 | memset(data, 0, 3 * (32 - j)); | 2411 | memset(data, 0, 3 * (32 - j)); |
2412 | data += 3 * (32 - j); | 2412 | data += 3 * (32 - j); |
2413 | } | 2413 | } |
2414 | } else { | 2414 | } else { |
2415 | j = vc->vc_font.height * 4; | 2415 | j = vc->vc_font.height * 4; |
2416 | for (i = 0; i < font->charcount; i++) { | 2416 | for (i = 0; i < font->charcount; i++) { |
2417 | memcpy(data, fontdata, j); | 2417 | memcpy(data, fontdata, j); |
2418 | memset(data + j, 0, 128 - j); | 2418 | memset(data + j, 0, 128 - j); |
2419 | data += 128; | 2419 | data += 128; |
2420 | fontdata += j; | 2420 | fontdata += j; |
2421 | } | 2421 | } |
2422 | } | 2422 | } |
2423 | return 0; | 2423 | return 0; |
2424 | } | 2424 | } |
2425 | 2425 | ||
2426 | static int fbcon_do_set_font(struct vc_data *vc, int w, int h, | 2426 | static int fbcon_do_set_font(struct vc_data *vc, int w, int h, |
2427 | const u8 * data, int userfont) | 2427 | const u8 * data, int userfont) |
2428 | { | 2428 | { |
2429 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 2429 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
2430 | struct fbcon_ops *ops = info->fbcon_par; | 2430 | struct fbcon_ops *ops = info->fbcon_par; |
2431 | struct display *p = &fb_display[vc->vc_num]; | 2431 | struct display *p = &fb_display[vc->vc_num]; |
2432 | int resize; | 2432 | int resize; |
2433 | int cnt; | 2433 | int cnt; |
2434 | char *old_data = NULL; | 2434 | char *old_data = NULL; |
2435 | 2435 | ||
2436 | if (CON_IS_VISIBLE(vc) && softback_lines) | 2436 | if (CON_IS_VISIBLE(vc) && softback_lines) |
2437 | fbcon_set_origin(vc); | 2437 | fbcon_set_origin(vc); |
2438 | 2438 | ||
2439 | resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); | 2439 | resize = (w != vc->vc_font.width) || (h != vc->vc_font.height); |
2440 | if (p->userfont) | 2440 | if (p->userfont) |
2441 | old_data = vc->vc_font.data; | 2441 | old_data = vc->vc_font.data; |
2442 | if (userfont) | 2442 | if (userfont) |
2443 | cnt = FNTCHARCNT(data); | 2443 | cnt = FNTCHARCNT(data); |
2444 | else | 2444 | else |
2445 | cnt = 256; | 2445 | cnt = 256; |
2446 | vc->vc_font.data = (void *)(p->fontdata = data); | 2446 | vc->vc_font.data = (void *)(p->fontdata = data); |
2447 | if ((p->userfont = userfont)) | 2447 | if ((p->userfont = userfont)) |
2448 | REFCOUNT(data)++; | 2448 | REFCOUNT(data)++; |
2449 | vc->vc_font.width = w; | 2449 | vc->vc_font.width = w; |
2450 | vc->vc_font.height = h; | 2450 | vc->vc_font.height = h; |
2451 | if (vc->vc_hi_font_mask && cnt == 256) { | 2451 | if (vc->vc_hi_font_mask && cnt == 256) { |
2452 | vc->vc_hi_font_mask = 0; | 2452 | vc->vc_hi_font_mask = 0; |
2453 | if (vc->vc_can_do_color) { | 2453 | if (vc->vc_can_do_color) { |
2454 | vc->vc_complement_mask >>= 1; | 2454 | vc->vc_complement_mask >>= 1; |
2455 | vc->vc_s_complement_mask >>= 1; | 2455 | vc->vc_s_complement_mask >>= 1; |
2456 | } | 2456 | } |
2457 | 2457 | ||
2458 | /* ++Edmund: reorder the attribute bits */ | 2458 | /* ++Edmund: reorder the attribute bits */ |
2459 | if (vc->vc_can_do_color) { | 2459 | if (vc->vc_can_do_color) { |
2460 | unsigned short *cp = | 2460 | unsigned short *cp = |
2461 | (unsigned short *) vc->vc_origin; | 2461 | (unsigned short *) vc->vc_origin; |
2462 | int count = vc->vc_screenbuf_size / 2; | 2462 | int count = vc->vc_screenbuf_size / 2; |
2463 | unsigned short c; | 2463 | unsigned short c; |
2464 | for (; count > 0; count--, cp++) { | 2464 | for (; count > 0; count--, cp++) { |
2465 | c = scr_readw(cp); | 2465 | c = scr_readw(cp); |
2466 | scr_writew(((c & 0xfe00) >> 1) | | 2466 | scr_writew(((c & 0xfe00) >> 1) | |
2467 | (c & 0xff), cp); | 2467 | (c & 0xff), cp); |
2468 | } | 2468 | } |
2469 | c = vc->vc_video_erase_char; | 2469 | c = vc->vc_video_erase_char; |
2470 | vc->vc_video_erase_char = | 2470 | vc->vc_video_erase_char = |
2471 | ((c & 0xfe00) >> 1) | (c & 0xff); | 2471 | ((c & 0xfe00) >> 1) | (c & 0xff); |
2472 | vc->vc_attr >>= 1; | 2472 | vc->vc_attr >>= 1; |
2473 | } | 2473 | } |
2474 | } else if (!vc->vc_hi_font_mask && cnt == 512) { | 2474 | } else if (!vc->vc_hi_font_mask && cnt == 512) { |
2475 | vc->vc_hi_font_mask = 0x100; | 2475 | vc->vc_hi_font_mask = 0x100; |
2476 | if (vc->vc_can_do_color) { | 2476 | if (vc->vc_can_do_color) { |
2477 | vc->vc_complement_mask <<= 1; | 2477 | vc->vc_complement_mask <<= 1; |
2478 | vc->vc_s_complement_mask <<= 1; | 2478 | vc->vc_s_complement_mask <<= 1; |
2479 | } | 2479 | } |
2480 | 2480 | ||
2481 | /* ++Edmund: reorder the attribute bits */ | 2481 | /* ++Edmund: reorder the attribute bits */ |
2482 | { | 2482 | { |
2483 | unsigned short *cp = | 2483 | unsigned short *cp = |
2484 | (unsigned short *) vc->vc_origin; | 2484 | (unsigned short *) vc->vc_origin; |
2485 | int count = vc->vc_screenbuf_size / 2; | 2485 | int count = vc->vc_screenbuf_size / 2; |
2486 | unsigned short c; | 2486 | unsigned short c; |
2487 | for (; count > 0; count--, cp++) { | 2487 | for (; count > 0; count--, cp++) { |
2488 | unsigned short newc; | 2488 | unsigned short newc; |
2489 | c = scr_readw(cp); | 2489 | c = scr_readw(cp); |
2490 | if (vc->vc_can_do_color) | 2490 | if (vc->vc_can_do_color) |
2491 | newc = | 2491 | newc = |
2492 | ((c & 0xff00) << 1) | (c & | 2492 | ((c & 0xff00) << 1) | (c & |
2493 | 0xff); | 2493 | 0xff); |
2494 | else | 2494 | else |
2495 | newc = c & ~0x100; | 2495 | newc = c & ~0x100; |
2496 | scr_writew(newc, cp); | 2496 | scr_writew(newc, cp); |
2497 | } | 2497 | } |
2498 | c = vc->vc_video_erase_char; | 2498 | c = vc->vc_video_erase_char; |
2499 | if (vc->vc_can_do_color) { | 2499 | if (vc->vc_can_do_color) { |
2500 | vc->vc_video_erase_char = | 2500 | vc->vc_video_erase_char = |
2501 | ((c & 0xff00) << 1) | (c & 0xff); | 2501 | ((c & 0xff00) << 1) | (c & 0xff); |
2502 | vc->vc_attr <<= 1; | 2502 | vc->vc_attr <<= 1; |
2503 | } else | 2503 | } else |
2504 | vc->vc_video_erase_char = c & ~0x100; | 2504 | vc->vc_video_erase_char = c & ~0x100; |
2505 | } | 2505 | } |
2506 | 2506 | ||
2507 | } | 2507 | } |
2508 | 2508 | ||
2509 | if (resize) { | 2509 | if (resize) { |
2510 | int cols, rows; | 2510 | int cols, rows; |
2511 | 2511 | ||
2512 | cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); | 2512 | cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); |
2513 | rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); | 2513 | rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); |
2514 | cols /= w; | 2514 | cols /= w; |
2515 | rows /= h; | 2515 | rows /= h; |
2516 | vc_resize(vc, cols, rows); | 2516 | vc_resize(vc, cols, rows); |
2517 | if (CON_IS_VISIBLE(vc) && softback_buf) | 2517 | if (CON_IS_VISIBLE(vc) && softback_buf) |
2518 | fbcon_update_softback(vc); | 2518 | fbcon_update_softback(vc); |
2519 | } else if (CON_IS_VISIBLE(vc) | 2519 | } else if (CON_IS_VISIBLE(vc) |
2520 | && vc->vc_mode == KD_TEXT) { | 2520 | && vc->vc_mode == KD_TEXT) { |
2521 | fbcon_clear_margins(vc, 0); | 2521 | fbcon_clear_margins(vc, 0); |
2522 | update_screen(vc); | 2522 | update_screen(vc); |
2523 | } | 2523 | } |
2524 | 2524 | ||
2525 | if (old_data && (--REFCOUNT(old_data) == 0)) | 2525 | if (old_data && (--REFCOUNT(old_data) == 0)) |
2526 | kfree(old_data - FONT_EXTRA_WORDS * sizeof(int)); | 2526 | kfree(old_data - FONT_EXTRA_WORDS * sizeof(int)); |
2527 | return 0; | 2527 | return 0; |
2528 | } | 2528 | } |
2529 | 2529 | ||
2530 | static int fbcon_copy_font(struct vc_data *vc, int con) | 2530 | static int fbcon_copy_font(struct vc_data *vc, int con) |
2531 | { | 2531 | { |
2532 | struct display *od = &fb_display[con]; | 2532 | struct display *od = &fb_display[con]; |
2533 | struct console_font *f = &vc->vc_font; | 2533 | struct console_font *f = &vc->vc_font; |
2534 | 2534 | ||
2535 | if (od->fontdata == f->data) | 2535 | if (od->fontdata == f->data) |
2536 | return 0; /* already the same font... */ | 2536 | return 0; /* already the same font... */ |
2537 | return fbcon_do_set_font(vc, f->width, f->height, od->fontdata, od->userfont); | 2537 | return fbcon_do_set_font(vc, f->width, f->height, od->fontdata, od->userfont); |
2538 | } | 2538 | } |
2539 | 2539 | ||
2540 | /* | 2540 | /* |
2541 | * User asked to set font; we are guaranteed that | 2541 | * User asked to set font; we are guaranteed that |
2542 | * a) width and height are in range 1..32 | 2542 | * a) width and height are in range 1..32 |
2543 | * b) charcount does not exceed 512 | 2543 | * b) charcount does not exceed 512 |
2544 | * but lets not assume that, since someone might someday want to use larger | 2544 | * but lets not assume that, since someone might someday want to use larger |
2545 | * fonts. And charcount of 512 is small for unicode support. | 2545 | * fonts. And charcount of 512 is small for unicode support. |
2546 | * | 2546 | * |
2547 | * However, user space gives the font in 32 rows , regardless of | 2547 | * However, user space gives the font in 32 rows , regardless of |
2548 | * actual font height. So a new API is needed if support for larger fonts | 2548 | * actual font height. So a new API is needed if support for larger fonts |
2549 | * is ever implemented. | 2549 | * is ever implemented. |
2550 | */ | 2550 | */ |
2551 | 2551 | ||
2552 | static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigned flags) | 2552 | static int fbcon_set_font(struct vc_data *vc, struct console_font *font, unsigned flags) |
2553 | { | 2553 | { |
2554 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 2554 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
2555 | unsigned charcount = font->charcount; | 2555 | unsigned charcount = font->charcount; |
2556 | int w = font->width; | 2556 | int w = font->width; |
2557 | int h = font->height; | 2557 | int h = font->height; |
2558 | int size; | 2558 | int size; |
2559 | int i, csum; | 2559 | int i, csum; |
2560 | u8 *new_data, *data = font->data; | 2560 | u8 *new_data, *data = font->data; |
2561 | int pitch = (font->width+7) >> 3; | 2561 | int pitch = (font->width+7) >> 3; |
2562 | 2562 | ||
2563 | /* Is there a reason why fbconsole couldn't handle any charcount >256? | 2563 | /* Is there a reason why fbconsole couldn't handle any charcount >256? |
2564 | * If not this check should be changed to charcount < 256 */ | 2564 | * If not this check should be changed to charcount < 256 */ |
2565 | if (charcount != 256 && charcount != 512) | 2565 | if (charcount != 256 && charcount != 512) |
2566 | return -EINVAL; | 2566 | return -EINVAL; |
2567 | 2567 | ||
2568 | /* Make sure drawing engine can handle the font */ | 2568 | /* Make sure drawing engine can handle the font */ |
2569 | if (!(info->pixmap.blit_x & (1 << (font->width - 1))) || | 2569 | if (!(info->pixmap.blit_x & (1 << (font->width - 1))) || |
2570 | !(info->pixmap.blit_y & (1 << (font->height - 1)))) | 2570 | !(info->pixmap.blit_y & (1 << (font->height - 1)))) |
2571 | return -EINVAL; | 2571 | return -EINVAL; |
2572 | 2572 | ||
2573 | /* Make sure driver can handle the font length */ | 2573 | /* Make sure driver can handle the font length */ |
2574 | if (fbcon_invalid_charcount(info, charcount)) | 2574 | if (fbcon_invalid_charcount(info, charcount)) |
2575 | return -EINVAL; | 2575 | return -EINVAL; |
2576 | 2576 | ||
2577 | size = h * pitch * charcount; | 2577 | size = h * pitch * charcount; |
2578 | 2578 | ||
2579 | new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER); | 2579 | new_data = kmalloc(FONT_EXTRA_WORDS * sizeof(int) + size, GFP_USER); |
2580 | 2580 | ||
2581 | if (!new_data) | 2581 | if (!new_data) |
2582 | return -ENOMEM; | 2582 | return -ENOMEM; |
2583 | 2583 | ||
2584 | new_data += FONT_EXTRA_WORDS * sizeof(int); | 2584 | new_data += FONT_EXTRA_WORDS * sizeof(int); |
2585 | FNTSIZE(new_data) = size; | 2585 | FNTSIZE(new_data) = size; |
2586 | FNTCHARCNT(new_data) = charcount; | 2586 | FNTCHARCNT(new_data) = charcount; |
2587 | REFCOUNT(new_data) = 0; /* usage counter */ | 2587 | REFCOUNT(new_data) = 0; /* usage counter */ |
2588 | for (i=0; i< charcount; i++) { | 2588 | for (i=0; i< charcount; i++) { |
2589 | memcpy(new_data + i*h*pitch, data + i*32*pitch, h*pitch); | 2589 | memcpy(new_data + i*h*pitch, data + i*32*pitch, h*pitch); |
2590 | } | 2590 | } |
2591 | 2591 | ||
2592 | /* Since linux has a nice crc32 function use it for counting font | 2592 | /* Since linux has a nice crc32 function use it for counting font |
2593 | * checksums. */ | 2593 | * checksums. */ |
2594 | csum = crc32(0, new_data, size); | 2594 | csum = crc32(0, new_data, size); |
2595 | 2595 | ||
2596 | FNTSUM(new_data) = csum; | 2596 | FNTSUM(new_data) = csum; |
2597 | /* Check if the same font is on some other console already */ | 2597 | /* Check if the same font is on some other console already */ |
2598 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 2598 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
2599 | struct vc_data *tmp = vc_cons[i].d; | 2599 | struct vc_data *tmp = vc_cons[i].d; |
2600 | 2600 | ||
2601 | if (fb_display[i].userfont && | 2601 | if (fb_display[i].userfont && |
2602 | fb_display[i].fontdata && | 2602 | fb_display[i].fontdata && |
2603 | FNTSUM(fb_display[i].fontdata) == csum && | 2603 | FNTSUM(fb_display[i].fontdata) == csum && |
2604 | FNTSIZE(fb_display[i].fontdata) == size && | 2604 | FNTSIZE(fb_display[i].fontdata) == size && |
2605 | tmp->vc_font.width == w && | 2605 | tmp->vc_font.width == w && |
2606 | !memcmp(fb_display[i].fontdata, new_data, size)) { | 2606 | !memcmp(fb_display[i].fontdata, new_data, size)) { |
2607 | kfree(new_data - FONT_EXTRA_WORDS * sizeof(int)); | 2607 | kfree(new_data - FONT_EXTRA_WORDS * sizeof(int)); |
2608 | new_data = (u8 *)fb_display[i].fontdata; | 2608 | new_data = (u8 *)fb_display[i].fontdata; |
2609 | break; | 2609 | break; |
2610 | } | 2610 | } |
2611 | } | 2611 | } |
2612 | return fbcon_do_set_font(vc, font->width, font->height, new_data, 1); | 2612 | return fbcon_do_set_font(vc, font->width, font->height, new_data, 1); |
2613 | } | 2613 | } |
2614 | 2614 | ||
2615 | static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name) | 2615 | static int fbcon_set_def_font(struct vc_data *vc, struct console_font *font, char *name) |
2616 | { | 2616 | { |
2617 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 2617 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
2618 | const struct font_desc *f; | 2618 | const struct font_desc *f; |
2619 | 2619 | ||
2620 | if (!name) | 2620 | if (!name) |
2621 | f = get_default_font(info->var.xres, info->var.yres, | 2621 | f = get_default_font(info->var.xres, info->var.yres, |
2622 | info->pixmap.blit_x, info->pixmap.blit_y); | 2622 | info->pixmap.blit_x, info->pixmap.blit_y); |
2623 | else if (!(f = find_font(name))) | 2623 | else if (!(f = find_font(name))) |
2624 | return -ENOENT; | 2624 | return -ENOENT; |
2625 | 2625 | ||
2626 | font->width = f->width; | 2626 | font->width = f->width; |
2627 | font->height = f->height; | 2627 | font->height = f->height; |
2628 | return fbcon_do_set_font(vc, f->width, f->height, f->data, 0); | 2628 | return fbcon_do_set_font(vc, f->width, f->height, f->data, 0); |
2629 | } | 2629 | } |
2630 | 2630 | ||
2631 | static u16 palette_red[16]; | 2631 | static u16 palette_red[16]; |
2632 | static u16 palette_green[16]; | 2632 | static u16 palette_green[16]; |
2633 | static u16 palette_blue[16]; | 2633 | static u16 palette_blue[16]; |
2634 | 2634 | ||
2635 | static struct fb_cmap palette_cmap = { | 2635 | static struct fb_cmap palette_cmap = { |
2636 | 0, 16, palette_red, palette_green, palette_blue, NULL | 2636 | 0, 16, palette_red, palette_green, palette_blue, NULL |
2637 | }; | 2637 | }; |
2638 | 2638 | ||
2639 | static int fbcon_set_palette(struct vc_data *vc, unsigned char *table) | 2639 | static int fbcon_set_palette(struct vc_data *vc, unsigned char *table) |
2640 | { | 2640 | { |
2641 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; | 2641 | struct fb_info *info = registered_fb[con2fb_map[vc->vc_num]]; |
2642 | int i, j, k, depth; | 2642 | int i, j, k, depth; |
2643 | u8 val; | 2643 | u8 val; |
2644 | 2644 | ||
2645 | if (fbcon_is_inactive(vc, info)) | 2645 | if (fbcon_is_inactive(vc, info)) |
2646 | return -EINVAL; | 2646 | return -EINVAL; |
2647 | 2647 | ||
2648 | if (!CON_IS_VISIBLE(vc)) | 2648 | if (!CON_IS_VISIBLE(vc)) |
2649 | return 0; | 2649 | return 0; |
2650 | 2650 | ||
2651 | depth = fb_get_color_depth(&info->var, &info->fix); | 2651 | depth = fb_get_color_depth(&info->var, &info->fix); |
2652 | if (depth > 3) { | 2652 | if (depth > 3) { |
2653 | for (i = j = 0; i < 16; i++) { | 2653 | for (i = j = 0; i < 16; i++) { |
2654 | k = table[i]; | 2654 | k = table[i]; |
2655 | val = vc->vc_palette[j++]; | 2655 | val = vc->vc_palette[j++]; |
2656 | palette_red[k] = (val << 8) | val; | 2656 | palette_red[k] = (val << 8) | val; |
2657 | val = vc->vc_palette[j++]; | 2657 | val = vc->vc_palette[j++]; |
2658 | palette_green[k] = (val << 8) | val; | 2658 | palette_green[k] = (val << 8) | val; |
2659 | val = vc->vc_palette[j++]; | 2659 | val = vc->vc_palette[j++]; |
2660 | palette_blue[k] = (val << 8) | val; | 2660 | palette_blue[k] = (val << 8) | val; |
2661 | } | 2661 | } |
2662 | palette_cmap.len = 16; | 2662 | palette_cmap.len = 16; |
2663 | palette_cmap.start = 0; | 2663 | palette_cmap.start = 0; |
2664 | /* | 2664 | /* |
2665 | * If framebuffer is capable of less than 16 colors, | 2665 | * If framebuffer is capable of less than 16 colors, |
2666 | * use default palette of fbcon. | 2666 | * use default palette of fbcon. |
2667 | */ | 2667 | */ |
2668 | } else | 2668 | } else |
2669 | fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap); | 2669 | fb_copy_cmap(fb_default_cmap(1 << depth), &palette_cmap); |
2670 | 2670 | ||
2671 | return fb_set_cmap(&palette_cmap, info); | 2671 | return fb_set_cmap(&palette_cmap, info); |
2672 | } | 2672 | } |
2673 | 2673 | ||
2674 | static u16 *fbcon_screen_pos(struct vc_data *vc, int offset) | 2674 | static u16 *fbcon_screen_pos(struct vc_data *vc, int offset) |
2675 | { | 2675 | { |
2676 | unsigned long p; | 2676 | unsigned long p; |
2677 | int line; | 2677 | int line; |
2678 | 2678 | ||
2679 | if (vc->vc_num != fg_console || !softback_lines) | 2679 | if (vc->vc_num != fg_console || !softback_lines) |
2680 | return (u16 *) (vc->vc_origin + offset); | 2680 | return (u16 *) (vc->vc_origin + offset); |
2681 | line = offset / vc->vc_size_row; | 2681 | line = offset / vc->vc_size_row; |
2682 | if (line >= softback_lines) | 2682 | if (line >= softback_lines) |
2683 | return (u16 *) (vc->vc_origin + offset - | 2683 | return (u16 *) (vc->vc_origin + offset - |
2684 | softback_lines * vc->vc_size_row); | 2684 | softback_lines * vc->vc_size_row); |
2685 | p = softback_curr + offset; | 2685 | p = softback_curr + offset; |
2686 | if (p >= softback_end) | 2686 | if (p >= softback_end) |
2687 | p += softback_buf - softback_end; | 2687 | p += softback_buf - softback_end; |
2688 | return (u16 *) p; | 2688 | return (u16 *) p; |
2689 | } | 2689 | } |
2690 | 2690 | ||
2691 | static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos, | 2691 | static unsigned long fbcon_getxy(struct vc_data *vc, unsigned long pos, |
2692 | int *px, int *py) | 2692 | int *px, int *py) |
2693 | { | 2693 | { |
2694 | unsigned long ret; | 2694 | unsigned long ret; |
2695 | int x, y; | 2695 | int x, y; |
2696 | 2696 | ||
2697 | if (pos >= vc->vc_origin && pos < vc->vc_scr_end) { | 2697 | if (pos >= vc->vc_origin && pos < vc->vc_scr_end) { |
2698 | unsigned long offset = (pos - vc->vc_origin) / 2; | 2698 | unsigned long offset = (pos - vc->vc_origin) / 2; |
2699 | 2699 | ||
2700 | x = offset % vc->vc_cols; | 2700 | x = offset % vc->vc_cols; |
2701 | y = offset / vc->vc_cols; | 2701 | y = offset / vc->vc_cols; |
2702 | if (vc->vc_num == fg_console) | 2702 | if (vc->vc_num == fg_console) |
2703 | y += softback_lines; | 2703 | y += softback_lines; |
2704 | ret = pos + (vc->vc_cols - x) * 2; | 2704 | ret = pos + (vc->vc_cols - x) * 2; |
2705 | } else if (vc->vc_num == fg_console && softback_lines) { | 2705 | } else if (vc->vc_num == fg_console && softback_lines) { |
2706 | unsigned long offset = pos - softback_curr; | 2706 | unsigned long offset = pos - softback_curr; |
2707 | 2707 | ||
2708 | if (pos < softback_curr) | 2708 | if (pos < softback_curr) |
2709 | offset += softback_end - softback_buf; | 2709 | offset += softback_end - softback_buf; |
2710 | offset /= 2; | 2710 | offset /= 2; |
2711 | x = offset % vc->vc_cols; | 2711 | x = offset % vc->vc_cols; |
2712 | y = offset / vc->vc_cols; | 2712 | y = offset / vc->vc_cols; |
2713 | ret = pos + (vc->vc_cols - x) * 2; | 2713 | ret = pos + (vc->vc_cols - x) * 2; |
2714 | if (ret == softback_end) | 2714 | if (ret == softback_end) |
2715 | ret = softback_buf; | 2715 | ret = softback_buf; |
2716 | if (ret == softback_in) | 2716 | if (ret == softback_in) |
2717 | ret = vc->vc_origin; | 2717 | ret = vc->vc_origin; |
2718 | } else { | 2718 | } else { |
2719 | /* Should not happen */ | 2719 | /* Should not happen */ |
2720 | x = y = 0; | 2720 | x = y = 0; |
2721 | ret = vc->vc_origin; | 2721 | ret = vc->vc_origin; |
2722 | } | 2722 | } |
2723 | if (px) | 2723 | if (px) |
2724 | *px = x; | 2724 | *px = x; |
2725 | if (py) | 2725 | if (py) |
2726 | *py = y; | 2726 | *py = y; |
2727 | return ret; | 2727 | return ret; |
2728 | } | 2728 | } |
2729 | 2729 | ||
2730 | /* As we might be inside of softback, we may work with non-contiguous buffer, | 2730 | /* As we might be inside of softback, we may work with non-contiguous buffer, |
2731 | that's why we have to use a separate routine. */ | 2731 | that's why we have to use a separate routine. */ |
2732 | static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt) | 2732 | static void fbcon_invert_region(struct vc_data *vc, u16 * p, int cnt) |
2733 | { | 2733 | { |
2734 | while (cnt--) { | 2734 | while (cnt--) { |
2735 | u16 a = scr_readw(p); | 2735 | u16 a = scr_readw(p); |
2736 | if (!vc->vc_can_do_color) | 2736 | if (!vc->vc_can_do_color) |
2737 | a ^= 0x0800; | 2737 | a ^= 0x0800; |
2738 | else if (vc->vc_hi_font_mask == 0x100) | 2738 | else if (vc->vc_hi_font_mask == 0x100) |
2739 | a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) | | 2739 | a = ((a) & 0x11ff) | (((a) & 0xe000) >> 4) | |
2740 | (((a) & 0x0e00) << 4); | 2740 | (((a) & 0x0e00) << 4); |
2741 | else | 2741 | else |
2742 | a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | | 2742 | a = ((a) & 0x88ff) | (((a) & 0x7000) >> 4) | |
2743 | (((a) & 0x0700) << 4); | 2743 | (((a) & 0x0700) << 4); |
2744 | scr_writew(a, p++); | 2744 | scr_writew(a, p++); |
2745 | if (p == (u16 *) softback_end) | 2745 | if (p == (u16 *) softback_end) |
2746 | p = (u16 *) softback_buf; | 2746 | p = (u16 *) softback_buf; |
2747 | if (p == (u16 *) softback_in) | 2747 | if (p == (u16 *) softback_in) |
2748 | p = (u16 *) vc->vc_origin; | 2748 | p = (u16 *) vc->vc_origin; |
2749 | } | 2749 | } |
2750 | } | 2750 | } |
2751 | 2751 | ||
2752 | static int fbcon_scrolldelta(struct vc_data *vc, int lines) | 2752 | static int fbcon_scrolldelta(struct vc_data *vc, int lines) |
2753 | { | 2753 | { |
2754 | struct fb_info *info = registered_fb[con2fb_map[fg_console]]; | 2754 | struct fb_info *info = registered_fb[con2fb_map[fg_console]]; |
2755 | struct fbcon_ops *ops = info->fbcon_par; | 2755 | struct fbcon_ops *ops = info->fbcon_par; |
2756 | struct display *disp = &fb_display[fg_console]; | 2756 | struct display *disp = &fb_display[fg_console]; |
2757 | int offset, limit, scrollback_old; | 2757 | int offset, limit, scrollback_old; |
2758 | 2758 | ||
2759 | if (softback_top) { | 2759 | if (softback_top) { |
2760 | if (vc->vc_num != fg_console) | 2760 | if (vc->vc_num != fg_console) |
2761 | return 0; | 2761 | return 0; |
2762 | if (vc->vc_mode != KD_TEXT || !lines) | 2762 | if (vc->vc_mode != KD_TEXT || !lines) |
2763 | return 0; | 2763 | return 0; |
2764 | if (logo_shown >= 0) { | 2764 | if (logo_shown >= 0) { |
2765 | struct vc_data *conp2 = vc_cons[logo_shown].d; | 2765 | struct vc_data *conp2 = vc_cons[logo_shown].d; |
2766 | 2766 | ||
2767 | if (conp2->vc_top == logo_lines | 2767 | if (conp2->vc_top == logo_lines |
2768 | && conp2->vc_bottom == conp2->vc_rows) | 2768 | && conp2->vc_bottom == conp2->vc_rows) |
2769 | conp2->vc_top = 0; | 2769 | conp2->vc_top = 0; |
2770 | if (logo_shown == vc->vc_num) { | 2770 | if (logo_shown == vc->vc_num) { |
2771 | unsigned long p, q; | 2771 | unsigned long p, q; |
2772 | int i; | 2772 | int i; |
2773 | 2773 | ||
2774 | p = softback_in; | 2774 | p = softback_in; |
2775 | q = vc->vc_origin + | 2775 | q = vc->vc_origin + |
2776 | logo_lines * vc->vc_size_row; | 2776 | logo_lines * vc->vc_size_row; |
2777 | for (i = 0; i < logo_lines; i++) { | 2777 | for (i = 0; i < logo_lines; i++) { |
2778 | if (p == softback_top) | 2778 | if (p == softback_top) |
2779 | break; | 2779 | break; |
2780 | if (p == softback_buf) | 2780 | if (p == softback_buf) |
2781 | p = softback_end; | 2781 | p = softback_end; |
2782 | p -= vc->vc_size_row; | 2782 | p -= vc->vc_size_row; |
2783 | q -= vc->vc_size_row; | 2783 | q -= vc->vc_size_row; |
2784 | scr_memcpyw((u16 *) q, (u16 *) p, | 2784 | scr_memcpyw((u16 *) q, (u16 *) p, |
2785 | vc->vc_size_row); | 2785 | vc->vc_size_row); |
2786 | } | 2786 | } |
2787 | softback_in = softback_curr = p; | 2787 | softback_in = softback_curr = p; |
2788 | update_region(vc, vc->vc_origin, | 2788 | update_region(vc, vc->vc_origin, |
2789 | logo_lines * vc->vc_cols); | 2789 | logo_lines * vc->vc_cols); |
2790 | } | 2790 | } |
2791 | logo_shown = FBCON_LOGO_CANSHOW; | 2791 | logo_shown = FBCON_LOGO_CANSHOW; |
2792 | } | 2792 | } |
2793 | fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK); | 2793 | fbcon_cursor(vc, CM_ERASE | CM_SOFTBACK); |
2794 | fbcon_redraw_softback(vc, disp, lines); | 2794 | fbcon_redraw_softback(vc, disp, lines); |
2795 | fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK); | 2795 | fbcon_cursor(vc, CM_DRAW | CM_SOFTBACK); |
2796 | return 0; | 2796 | return 0; |
2797 | } | 2797 | } |
2798 | 2798 | ||
2799 | if (!scrollback_phys_max) | 2799 | if (!scrollback_phys_max) |
2800 | return -ENOSYS; | 2800 | return -ENOSYS; |
2801 | 2801 | ||
2802 | scrollback_old = scrollback_current; | 2802 | scrollback_old = scrollback_current; |
2803 | scrollback_current -= lines; | 2803 | scrollback_current -= lines; |
2804 | if (scrollback_current < 0) | 2804 | if (scrollback_current < 0) |
2805 | scrollback_current = 0; | 2805 | scrollback_current = 0; |
2806 | else if (scrollback_current > scrollback_max) | 2806 | else if (scrollback_current > scrollback_max) |
2807 | scrollback_current = scrollback_max; | 2807 | scrollback_current = scrollback_max; |
2808 | if (scrollback_current == scrollback_old) | 2808 | if (scrollback_current == scrollback_old) |
2809 | return 0; | 2809 | return 0; |
2810 | 2810 | ||
2811 | if (fbcon_is_inactive(vc, info)) | 2811 | if (fbcon_is_inactive(vc, info)) |
2812 | return 0; | 2812 | return 0; |
2813 | 2813 | ||
2814 | fbcon_cursor(vc, CM_ERASE); | 2814 | fbcon_cursor(vc, CM_ERASE); |
2815 | 2815 | ||
2816 | offset = disp->yscroll - scrollback_current; | 2816 | offset = disp->yscroll - scrollback_current; |
2817 | limit = disp->vrows; | 2817 | limit = disp->vrows; |
2818 | switch (disp->scrollmode) { | 2818 | switch (disp->scrollmode) { |
2819 | case SCROLL_WRAP_MOVE: | 2819 | case SCROLL_WRAP_MOVE: |
2820 | info->var.vmode |= FB_VMODE_YWRAP; | 2820 | info->var.vmode |= FB_VMODE_YWRAP; |
2821 | break; | 2821 | break; |
2822 | case SCROLL_PAN_MOVE: | 2822 | case SCROLL_PAN_MOVE: |
2823 | case SCROLL_PAN_REDRAW: | 2823 | case SCROLL_PAN_REDRAW: |
2824 | limit -= vc->vc_rows; | 2824 | limit -= vc->vc_rows; |
2825 | info->var.vmode &= ~FB_VMODE_YWRAP; | 2825 | info->var.vmode &= ~FB_VMODE_YWRAP; |
2826 | break; | 2826 | break; |
2827 | } | 2827 | } |
2828 | if (offset < 0) | 2828 | if (offset < 0) |
2829 | offset += limit; | 2829 | offset += limit; |
2830 | else if (offset >= limit) | 2830 | else if (offset >= limit) |
2831 | offset -= limit; | 2831 | offset -= limit; |
2832 | 2832 | ||
2833 | ops->var.xoffset = 0; | 2833 | ops->var.xoffset = 0; |
2834 | ops->var.yoffset = offset * vc->vc_font.height; | 2834 | ops->var.yoffset = offset * vc->vc_font.height; |
2835 | ops->update_start(info); | 2835 | ops->update_start(info); |
2836 | 2836 | ||
2837 | if (!scrollback_current) | 2837 | if (!scrollback_current) |
2838 | fbcon_cursor(vc, CM_DRAW); | 2838 | fbcon_cursor(vc, CM_DRAW); |
2839 | return 0; | 2839 | return 0; |
2840 | } | 2840 | } |
2841 | 2841 | ||
2842 | static int fbcon_set_origin(struct vc_data *vc) | 2842 | static int fbcon_set_origin(struct vc_data *vc) |
2843 | { | 2843 | { |
2844 | if (softback_lines) | 2844 | if (softback_lines) |
2845 | fbcon_scrolldelta(vc, softback_lines); | 2845 | fbcon_scrolldelta(vc, softback_lines); |
2846 | return 0; | 2846 | return 0; |
2847 | } | 2847 | } |
2848 | 2848 | ||
2849 | static void fbcon_suspended(struct fb_info *info) | 2849 | static void fbcon_suspended(struct fb_info *info) |
2850 | { | 2850 | { |
2851 | struct vc_data *vc = NULL; | 2851 | struct vc_data *vc = NULL; |
2852 | struct fbcon_ops *ops = info->fbcon_par; | 2852 | struct fbcon_ops *ops = info->fbcon_par; |
2853 | 2853 | ||
2854 | if (!ops || ops->currcon < 0) | 2854 | if (!ops || ops->currcon < 0) |
2855 | return; | 2855 | return; |
2856 | vc = vc_cons[ops->currcon].d; | 2856 | vc = vc_cons[ops->currcon].d; |
2857 | 2857 | ||
2858 | /* Clear cursor, restore saved data */ | 2858 | /* Clear cursor, restore saved data */ |
2859 | fbcon_cursor(vc, CM_ERASE); | 2859 | fbcon_cursor(vc, CM_ERASE); |
2860 | } | 2860 | } |
2861 | 2861 | ||
2862 | static void fbcon_resumed(struct fb_info *info) | 2862 | static void fbcon_resumed(struct fb_info *info) |
2863 | { | 2863 | { |
2864 | struct vc_data *vc; | 2864 | struct vc_data *vc; |
2865 | struct fbcon_ops *ops = info->fbcon_par; | 2865 | struct fbcon_ops *ops = info->fbcon_par; |
2866 | 2866 | ||
2867 | if (!ops || ops->currcon < 0) | 2867 | if (!ops || ops->currcon < 0) |
2868 | return; | 2868 | return; |
2869 | vc = vc_cons[ops->currcon].d; | 2869 | vc = vc_cons[ops->currcon].d; |
2870 | 2870 | ||
2871 | update_screen(vc); | 2871 | update_screen(vc); |
2872 | } | 2872 | } |
2873 | 2873 | ||
2874 | static void fbcon_modechanged(struct fb_info *info) | 2874 | static void fbcon_modechanged(struct fb_info *info) |
2875 | { | 2875 | { |
2876 | struct fbcon_ops *ops = info->fbcon_par; | 2876 | struct fbcon_ops *ops = info->fbcon_par; |
2877 | struct vc_data *vc; | 2877 | struct vc_data *vc; |
2878 | struct display *p; | 2878 | struct display *p; |
2879 | int rows, cols; | 2879 | int rows, cols; |
2880 | 2880 | ||
2881 | if (!ops || ops->currcon < 0) | 2881 | if (!ops || ops->currcon < 0) |
2882 | return; | 2882 | return; |
2883 | vc = vc_cons[ops->currcon].d; | 2883 | vc = vc_cons[ops->currcon].d; |
2884 | if (vc->vc_mode != KD_TEXT || | 2884 | if (vc->vc_mode != KD_TEXT || |
2885 | registered_fb[con2fb_map[ops->currcon]] != info) | 2885 | registered_fb[con2fb_map[ops->currcon]] != info) |
2886 | return; | 2886 | return; |
2887 | 2887 | ||
2888 | p = &fb_display[vc->vc_num]; | 2888 | p = &fb_display[vc->vc_num]; |
2889 | set_blitting_type(vc, info); | 2889 | set_blitting_type(vc, info); |
2890 | 2890 | ||
2891 | if (CON_IS_VISIBLE(vc)) { | 2891 | if (CON_IS_VISIBLE(vc)) { |
2892 | var_to_display(p, &info->var, info); | 2892 | var_to_display(p, &info->var, info); |
2893 | cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); | 2893 | cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); |
2894 | rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); | 2894 | rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); |
2895 | cols /= vc->vc_font.width; | 2895 | cols /= vc->vc_font.width; |
2896 | rows /= vc->vc_font.height; | 2896 | rows /= vc->vc_font.height; |
2897 | vc_resize(vc, cols, rows); | 2897 | vc_resize(vc, cols, rows); |
2898 | updatescrollmode(p, info, vc); | 2898 | updatescrollmode(p, info, vc); |
2899 | scrollback_max = 0; | 2899 | scrollback_max = 0; |
2900 | scrollback_current = 0; | 2900 | scrollback_current = 0; |
2901 | 2901 | ||
2902 | if (!fbcon_is_inactive(vc, info)) { | 2902 | if (!fbcon_is_inactive(vc, info)) { |
2903 | ops->var.xoffset = ops->var.yoffset = p->yscroll = 0; | 2903 | ops->var.xoffset = ops->var.yoffset = p->yscroll = 0; |
2904 | ops->update_start(info); | 2904 | ops->update_start(info); |
2905 | } | 2905 | } |
2906 | 2906 | ||
2907 | fbcon_set_palette(vc, color_table); | 2907 | fbcon_set_palette(vc, color_table); |
2908 | update_screen(vc); | 2908 | update_screen(vc); |
2909 | if (softback_buf) | 2909 | if (softback_buf) |
2910 | fbcon_update_softback(vc); | 2910 | fbcon_update_softback(vc); |
2911 | } | 2911 | } |
2912 | } | 2912 | } |
2913 | 2913 | ||
2914 | static void fbcon_set_all_vcs(struct fb_info *info) | 2914 | static void fbcon_set_all_vcs(struct fb_info *info) |
2915 | { | 2915 | { |
2916 | struct fbcon_ops *ops = info->fbcon_par; | 2916 | struct fbcon_ops *ops = info->fbcon_par; |
2917 | struct vc_data *vc; | 2917 | struct vc_data *vc; |
2918 | struct display *p; | 2918 | struct display *p; |
2919 | int i, rows, cols, fg = -1; | 2919 | int i, rows, cols, fg = -1; |
2920 | 2920 | ||
2921 | if (!ops || ops->currcon < 0) | 2921 | if (!ops || ops->currcon < 0) |
2922 | return; | 2922 | return; |
2923 | 2923 | ||
2924 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 2924 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
2925 | vc = vc_cons[i].d; | 2925 | vc = vc_cons[i].d; |
2926 | if (!vc || vc->vc_mode != KD_TEXT || | 2926 | if (!vc || vc->vc_mode != KD_TEXT || |
2927 | registered_fb[con2fb_map[i]] != info) | 2927 | registered_fb[con2fb_map[i]] != info) |
2928 | continue; | 2928 | continue; |
2929 | 2929 | ||
2930 | if (CON_IS_VISIBLE(vc)) { | 2930 | if (CON_IS_VISIBLE(vc)) { |
2931 | fg = i; | 2931 | fg = i; |
2932 | continue; | 2932 | continue; |
2933 | } | 2933 | } |
2934 | 2934 | ||
2935 | p = &fb_display[vc->vc_num]; | 2935 | p = &fb_display[vc->vc_num]; |
2936 | set_blitting_type(vc, info); | 2936 | set_blitting_type(vc, info); |
2937 | var_to_display(p, &info->var, info); | 2937 | var_to_display(p, &info->var, info); |
2938 | cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); | 2938 | cols = FBCON_SWAP(ops->rotate, info->var.xres, info->var.yres); |
2939 | rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); | 2939 | rows = FBCON_SWAP(ops->rotate, info->var.yres, info->var.xres); |
2940 | cols /= vc->vc_font.width; | 2940 | cols /= vc->vc_font.width; |
2941 | rows /= vc->vc_font.height; | 2941 | rows /= vc->vc_font.height; |
2942 | vc_resize(vc, cols, rows); | 2942 | vc_resize(vc, cols, rows); |
2943 | } | 2943 | } |
2944 | 2944 | ||
2945 | if (fg != -1) | 2945 | if (fg != -1) |
2946 | fbcon_modechanged(info); | 2946 | fbcon_modechanged(info); |
2947 | } | 2947 | } |
2948 | 2948 | ||
2949 | static int fbcon_mode_deleted(struct fb_info *info, | 2949 | static int fbcon_mode_deleted(struct fb_info *info, |
2950 | struct fb_videomode *mode) | 2950 | struct fb_videomode *mode) |
2951 | { | 2951 | { |
2952 | struct fb_info *fb_info; | 2952 | struct fb_info *fb_info; |
2953 | struct display *p; | 2953 | struct display *p; |
2954 | int i, j, found = 0; | 2954 | int i, j, found = 0; |
2955 | 2955 | ||
2956 | /* before deletion, ensure that mode is not in use */ | 2956 | /* before deletion, ensure that mode is not in use */ |
2957 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 2957 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
2958 | j = con2fb_map[i]; | 2958 | j = con2fb_map[i]; |
2959 | if (j == -1) | 2959 | if (j == -1) |
2960 | continue; | 2960 | continue; |
2961 | fb_info = registered_fb[j]; | 2961 | fb_info = registered_fb[j]; |
2962 | if (fb_info != info) | 2962 | if (fb_info != info) |
2963 | continue; | 2963 | continue; |
2964 | p = &fb_display[i]; | 2964 | p = &fb_display[i]; |
2965 | if (!p || !p->mode) | 2965 | if (!p || !p->mode) |
2966 | continue; | 2966 | continue; |
2967 | if (fb_mode_is_equal(p->mode, mode)) { | 2967 | if (fb_mode_is_equal(p->mode, mode)) { |
2968 | found = 1; | 2968 | found = 1; |
2969 | break; | 2969 | break; |
2970 | } | 2970 | } |
2971 | } | 2971 | } |
2972 | return found; | 2972 | return found; |
2973 | } | 2973 | } |
2974 | 2974 | ||
2975 | #ifdef CONFIG_VT_HW_CONSOLE_BINDING | 2975 | #ifdef CONFIG_VT_HW_CONSOLE_BINDING |
2976 | static int fbcon_unbind(void) | 2976 | static int fbcon_unbind(void) |
2977 | { | 2977 | { |
2978 | int ret; | 2978 | int ret; |
2979 | 2979 | ||
2980 | ret = unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, | 2980 | ret = unbind_con_driver(&fb_con, first_fb_vc, last_fb_vc, |
2981 | fbcon_is_default); | 2981 | fbcon_is_default); |
2982 | 2982 | ||
2983 | if (!ret) | 2983 | if (!ret) |
2984 | fbcon_has_console_bind = 0; | 2984 | fbcon_has_console_bind = 0; |
2985 | 2985 | ||
2986 | return ret; | 2986 | return ret; |
2987 | } | 2987 | } |
2988 | #else | 2988 | #else |
2989 | static inline int fbcon_unbind(void) | 2989 | static inline int fbcon_unbind(void) |
2990 | { | 2990 | { |
2991 | return -EINVAL; | 2991 | return -EINVAL; |
2992 | } | 2992 | } |
2993 | #endif /* CONFIG_VT_HW_CONSOLE_BINDING */ | 2993 | #endif /* CONFIG_VT_HW_CONSOLE_BINDING */ |
2994 | 2994 | ||
2995 | static int fbcon_fb_unbind(int idx) | 2995 | static int fbcon_fb_unbind(int idx) |
2996 | { | 2996 | { |
2997 | int i, new_idx = -1, ret = 0; | 2997 | int i, new_idx = -1, ret = 0; |
2998 | 2998 | ||
2999 | if (!fbcon_has_console_bind) | 2999 | if (!fbcon_has_console_bind) |
3000 | return 0; | 3000 | return 0; |
3001 | 3001 | ||
3002 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 3002 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
3003 | if (con2fb_map[i] != idx && | 3003 | if (con2fb_map[i] != idx && |
3004 | con2fb_map[i] != -1) { | 3004 | con2fb_map[i] != -1) { |
3005 | new_idx = i; | 3005 | new_idx = i; |
3006 | break; | 3006 | break; |
3007 | } | 3007 | } |
3008 | } | 3008 | } |
3009 | 3009 | ||
3010 | if (new_idx != -1) { | 3010 | if (new_idx != -1) { |
3011 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 3011 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
3012 | if (con2fb_map[i] == idx) | 3012 | if (con2fb_map[i] == idx) |
3013 | set_con2fb_map(i, new_idx, 0); | 3013 | set_con2fb_map(i, new_idx, 0); |
3014 | } | 3014 | } |
3015 | } else | 3015 | } else |
3016 | ret = fbcon_unbind(); | 3016 | ret = fbcon_unbind(); |
3017 | 3017 | ||
3018 | return ret; | 3018 | return ret; |
3019 | } | 3019 | } |
3020 | 3020 | ||
3021 | static int fbcon_fb_unregistered(struct fb_info *info) | 3021 | static int fbcon_fb_unregistered(struct fb_info *info) |
3022 | { | 3022 | { |
3023 | int i, idx; | 3023 | int i, idx; |
3024 | 3024 | ||
3025 | idx = info->node; | 3025 | idx = info->node; |
3026 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 3026 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
3027 | if (con2fb_map[i] == idx) | 3027 | if (con2fb_map[i] == idx) |
3028 | con2fb_map[i] = -1; | 3028 | con2fb_map[i] = -1; |
3029 | } | 3029 | } |
3030 | 3030 | ||
3031 | if (idx == info_idx) { | 3031 | if (idx == info_idx) { |
3032 | info_idx = -1; | 3032 | info_idx = -1; |
3033 | 3033 | ||
3034 | for (i = 0; i < FB_MAX; i++) { | 3034 | for (i = 0; i < FB_MAX; i++) { |
3035 | if (registered_fb[i] != NULL) { | 3035 | if (registered_fb[i] != NULL) { |
3036 | info_idx = i; | 3036 | info_idx = i; |
3037 | break; | 3037 | break; |
3038 | } | 3038 | } |
3039 | } | 3039 | } |
3040 | } | 3040 | } |
3041 | 3041 | ||
3042 | if (info_idx != -1) { | 3042 | if (info_idx != -1) { |
3043 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 3043 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
3044 | if (con2fb_map[i] == -1) | 3044 | if (con2fb_map[i] == -1) |
3045 | con2fb_map[i] = info_idx; | 3045 | con2fb_map[i] = info_idx; |
3046 | } | 3046 | } |
3047 | } | 3047 | } |
3048 | 3048 | ||
3049 | if (primary_device == idx) | 3049 | if (primary_device == idx) |
3050 | primary_device = -1; | 3050 | primary_device = -1; |
3051 | 3051 | ||
3052 | if (!num_registered_fb) | 3052 | if (!num_registered_fb) |
3053 | unregister_con_driver(&fb_con); | 3053 | unregister_con_driver(&fb_con); |
3054 | 3054 | ||
3055 | return 0; | 3055 | return 0; |
3056 | } | 3056 | } |
3057 | 3057 | ||
3058 | static void fbcon_remap_all(int idx) | 3058 | static void fbcon_remap_all(int idx) |
3059 | { | 3059 | { |
3060 | int i; | 3060 | int i; |
3061 | for (i = first_fb_vc; i <= last_fb_vc; i++) | 3061 | for (i = first_fb_vc; i <= last_fb_vc; i++) |
3062 | set_con2fb_map(i, idx, 0); | 3062 | set_con2fb_map(i, idx, 0); |
3063 | 3063 | ||
3064 | if (con_is_bound(&fb_con)) { | 3064 | if (con_is_bound(&fb_con)) { |
3065 | printk(KERN_INFO "fbcon: Remapping primary device, " | 3065 | printk(KERN_INFO "fbcon: Remapping primary device, " |
3066 | "fb%i, to tty %i-%i\n", idx, | 3066 | "fb%i, to tty %i-%i\n", idx, |
3067 | first_fb_vc + 1, last_fb_vc + 1); | 3067 | first_fb_vc + 1, last_fb_vc + 1); |
3068 | info_idx = idx; | 3068 | info_idx = idx; |
3069 | } | 3069 | } |
3070 | } | 3070 | } |
3071 | 3071 | ||
3072 | #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY | 3072 | #ifdef CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY |
3073 | static void fbcon_select_primary(struct fb_info *info) | 3073 | static void fbcon_select_primary(struct fb_info *info) |
3074 | { | 3074 | { |
3075 | if (!map_override && primary_device == -1 && | 3075 | if (!map_override && primary_device == -1 && |
3076 | fb_is_primary_device(info)) { | 3076 | fb_is_primary_device(info)) { |
3077 | int i; | 3077 | int i; |
3078 | 3078 | ||
3079 | printk(KERN_INFO "fbcon: %s (fb%i) is primary device\n", | 3079 | printk(KERN_INFO "fbcon: %s (fb%i) is primary device\n", |
3080 | info->fix.id, info->node); | 3080 | info->fix.id, info->node); |
3081 | primary_device = info->node; | 3081 | primary_device = info->node; |
3082 | 3082 | ||
3083 | for (i = first_fb_vc; i <= last_fb_vc; i++) | 3083 | for (i = first_fb_vc; i <= last_fb_vc; i++) |
3084 | con2fb_map_boot[i] = primary_device; | 3084 | con2fb_map_boot[i] = primary_device; |
3085 | 3085 | ||
3086 | if (con_is_bound(&fb_con)) { | 3086 | if (con_is_bound(&fb_con)) { |
3087 | printk(KERN_INFO "fbcon: Remapping primary device, " | 3087 | printk(KERN_INFO "fbcon: Remapping primary device, " |
3088 | "fb%i, to tty %i-%i\n", info->node, | 3088 | "fb%i, to tty %i-%i\n", info->node, |
3089 | first_fb_vc + 1, last_fb_vc + 1); | 3089 | first_fb_vc + 1, last_fb_vc + 1); |
3090 | info_idx = primary_device; | 3090 | info_idx = primary_device; |
3091 | } | 3091 | } |
3092 | } | 3092 | } |
3093 | 3093 | ||
3094 | } | 3094 | } |
3095 | #else | 3095 | #else |
3096 | static inline void fbcon_select_primary(struct fb_info *info) | 3096 | static inline void fbcon_select_primary(struct fb_info *info) |
3097 | { | 3097 | { |
3098 | return; | 3098 | return; |
3099 | } | 3099 | } |
3100 | #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */ | 3100 | #endif /* CONFIG_FRAMEBUFFER_DETECT_PRIMARY */ |
3101 | 3101 | ||
3102 | static int fbcon_fb_registered(struct fb_info *info) | 3102 | static int fbcon_fb_registered(struct fb_info *info) |
3103 | { | 3103 | { |
3104 | int ret = 0, i, idx; | 3104 | int ret = 0, i, idx; |
3105 | 3105 | ||
3106 | idx = info->node; | 3106 | idx = info->node; |
3107 | fbcon_select_primary(info); | 3107 | fbcon_select_primary(info); |
3108 | 3108 | ||
3109 | if (info_idx == -1) { | 3109 | if (info_idx == -1) { |
3110 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 3110 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
3111 | if (con2fb_map_boot[i] == idx) { | 3111 | if (con2fb_map_boot[i] == idx) { |
3112 | info_idx = idx; | 3112 | info_idx = idx; |
3113 | break; | 3113 | break; |
3114 | } | 3114 | } |
3115 | } | 3115 | } |
3116 | 3116 | ||
3117 | if (info_idx != -1) | 3117 | if (info_idx != -1) |
3118 | ret = fbcon_takeover(1); | 3118 | ret = fbcon_takeover(1); |
3119 | } else { | 3119 | } else { |
3120 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 3120 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
3121 | if (con2fb_map_boot[i] == idx) | 3121 | if (con2fb_map_boot[i] == idx) |
3122 | set_con2fb_map(i, idx, 0); | 3122 | set_con2fb_map(i, idx, 0); |
3123 | } | 3123 | } |
3124 | } | 3124 | } |
3125 | 3125 | ||
3126 | return ret; | 3126 | return ret; |
3127 | } | 3127 | } |
3128 | 3128 | ||
3129 | static void fbcon_fb_blanked(struct fb_info *info, int blank) | 3129 | static void fbcon_fb_blanked(struct fb_info *info, int blank) |
3130 | { | 3130 | { |
3131 | struct fbcon_ops *ops = info->fbcon_par; | 3131 | struct fbcon_ops *ops = info->fbcon_par; |
3132 | struct vc_data *vc; | 3132 | struct vc_data *vc; |
3133 | 3133 | ||
3134 | if (!ops || ops->currcon < 0) | 3134 | if (!ops || ops->currcon < 0) |
3135 | return; | 3135 | return; |
3136 | 3136 | ||
3137 | vc = vc_cons[ops->currcon].d; | 3137 | vc = vc_cons[ops->currcon].d; |
3138 | if (vc->vc_mode != KD_TEXT || | 3138 | if (vc->vc_mode != KD_TEXT || |
3139 | registered_fb[con2fb_map[ops->currcon]] != info) | 3139 | registered_fb[con2fb_map[ops->currcon]] != info) |
3140 | return; | 3140 | return; |
3141 | 3141 | ||
3142 | if (CON_IS_VISIBLE(vc)) { | 3142 | if (CON_IS_VISIBLE(vc)) { |
3143 | if (blank) | 3143 | if (blank) |
3144 | do_blank_screen(0); | 3144 | do_blank_screen(0); |
3145 | else | 3145 | else |
3146 | do_unblank_screen(0); | 3146 | do_unblank_screen(0); |
3147 | } | 3147 | } |
3148 | ops->blank_state = blank; | 3148 | ops->blank_state = blank; |
3149 | } | 3149 | } |
3150 | 3150 | ||
3151 | static void fbcon_new_modelist(struct fb_info *info) | 3151 | static void fbcon_new_modelist(struct fb_info *info) |
3152 | { | 3152 | { |
3153 | int i; | 3153 | int i; |
3154 | struct vc_data *vc; | 3154 | struct vc_data *vc; |
3155 | struct fb_var_screeninfo var; | 3155 | struct fb_var_screeninfo var; |
3156 | const struct fb_videomode *mode; | 3156 | const struct fb_videomode *mode; |
3157 | 3157 | ||
3158 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 3158 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
3159 | if (registered_fb[con2fb_map[i]] != info) | 3159 | if (registered_fb[con2fb_map[i]] != info) |
3160 | continue; | 3160 | continue; |
3161 | if (!fb_display[i].mode) | 3161 | if (!fb_display[i].mode) |
3162 | continue; | 3162 | continue; |
3163 | vc = vc_cons[i].d; | 3163 | vc = vc_cons[i].d; |
3164 | display_to_var(&var, &fb_display[i]); | 3164 | display_to_var(&var, &fb_display[i]); |
3165 | mode = fb_find_nearest_mode(fb_display[i].mode, | 3165 | mode = fb_find_nearest_mode(fb_display[i].mode, |
3166 | &info->modelist); | 3166 | &info->modelist); |
3167 | fb_videomode_to_var(&var, mode); | 3167 | fb_videomode_to_var(&var, mode); |
3168 | fbcon_set_disp(info, &var, vc->vc_num); | 3168 | fbcon_set_disp(info, &var, vc->vc_num); |
3169 | } | 3169 | } |
3170 | } | 3170 | } |
3171 | 3171 | ||
3172 | static void fbcon_get_requirement(struct fb_info *info, | 3172 | static void fbcon_get_requirement(struct fb_info *info, |
3173 | struct fb_blit_caps *caps) | 3173 | struct fb_blit_caps *caps) |
3174 | { | 3174 | { |
3175 | struct vc_data *vc; | 3175 | struct vc_data *vc; |
3176 | struct display *p; | 3176 | struct display *p; |
3177 | 3177 | ||
3178 | if (caps->flags) { | 3178 | if (caps->flags) { |
3179 | int i, charcnt; | 3179 | int i, charcnt; |
3180 | 3180 | ||
3181 | for (i = first_fb_vc; i <= last_fb_vc; i++) { | 3181 | for (i = first_fb_vc; i <= last_fb_vc; i++) { |
3182 | vc = vc_cons[i].d; | 3182 | vc = vc_cons[i].d; |
3183 | if (vc && vc->vc_mode == KD_TEXT && | 3183 | if (vc && vc->vc_mode == KD_TEXT && |
3184 | info->node == con2fb_map[i]) { | 3184 | info->node == con2fb_map[i]) { |
3185 | p = &fb_display[i]; | 3185 | p = &fb_display[i]; |
3186 | caps->x |= 1 << (vc->vc_font.width - 1); | 3186 | caps->x |= 1 << (vc->vc_font.width - 1); |
3187 | caps->y |= 1 << (vc->vc_font.height - 1); | 3187 | caps->y |= 1 << (vc->vc_font.height - 1); |
3188 | charcnt = (p->userfont) ? | 3188 | charcnt = (p->userfont) ? |
3189 | FNTCHARCNT(p->fontdata) : 256; | 3189 | FNTCHARCNT(p->fontdata) : 256; |
3190 | if (caps->len < charcnt) | 3190 | if (caps->len < charcnt) |
3191 | caps->len = charcnt; | 3191 | caps->len = charcnt; |
3192 | } | 3192 | } |
3193 | } | 3193 | } |
3194 | } else { | 3194 | } else { |
3195 | vc = vc_cons[fg_console].d; | 3195 | vc = vc_cons[fg_console].d; |
3196 | 3196 | ||
3197 | if (vc && vc->vc_mode == KD_TEXT && | 3197 | if (vc && vc->vc_mode == KD_TEXT && |
3198 | info->node == con2fb_map[fg_console]) { | 3198 | info->node == con2fb_map[fg_console]) { |
3199 | p = &fb_display[fg_console]; | 3199 | p = &fb_display[fg_console]; |
3200 | caps->x = 1 << (vc->vc_font.width - 1); | 3200 | caps->x = 1 << (vc->vc_font.width - 1); |
3201 | caps->y = 1 << (vc->vc_font.height - 1); | 3201 | caps->y = 1 << (vc->vc_font.height - 1); |
3202 | caps->len = (p->userfont) ? | 3202 | caps->len = (p->userfont) ? |
3203 | FNTCHARCNT(p->fontdata) : 256; | 3203 | FNTCHARCNT(p->fontdata) : 256; |
3204 | } | 3204 | } |
3205 | } | 3205 | } |
3206 | } | 3206 | } |
3207 | 3207 | ||
3208 | static int fbcon_event_notify(struct notifier_block *self, | 3208 | static int fbcon_event_notify(struct notifier_block *self, |
3209 | unsigned long action, void *data) | 3209 | unsigned long action, void *data) |
3210 | { | 3210 | { |
3211 | struct fb_event *event = data; | 3211 | struct fb_event *event = data; |
3212 | struct fb_info *info = event->info; | 3212 | struct fb_info *info = event->info; |
3213 | struct fb_videomode *mode; | 3213 | struct fb_videomode *mode; |
3214 | struct fb_con2fbmap *con2fb; | 3214 | struct fb_con2fbmap *con2fb; |
3215 | struct fb_blit_caps *caps; | 3215 | struct fb_blit_caps *caps; |
3216 | int idx, ret = 0; | 3216 | int idx, ret = 0; |
3217 | 3217 | ||
3218 | /* | 3218 | /* |
3219 | * ignore all events except driver registration and deregistration | 3219 | * ignore all events except driver registration and deregistration |
3220 | * if fbcon is not active | 3220 | * if fbcon is not active |
3221 | */ | 3221 | */ |
3222 | if (fbcon_has_exited && !(action == FB_EVENT_FB_REGISTERED || | 3222 | if (fbcon_has_exited && !(action == FB_EVENT_FB_REGISTERED || |
3223 | action == FB_EVENT_FB_UNREGISTERED)) | 3223 | action == FB_EVENT_FB_UNREGISTERED)) |
3224 | goto done; | 3224 | goto done; |
3225 | 3225 | ||
3226 | switch(action) { | 3226 | switch(action) { |
3227 | case FB_EVENT_SUSPEND: | 3227 | case FB_EVENT_SUSPEND: |
3228 | fbcon_suspended(info); | 3228 | fbcon_suspended(info); |
3229 | break; | 3229 | break; |
3230 | case FB_EVENT_RESUME: | 3230 | case FB_EVENT_RESUME: |
3231 | fbcon_resumed(info); | 3231 | fbcon_resumed(info); |
3232 | break; | 3232 | break; |
3233 | case FB_EVENT_MODE_CHANGE: | 3233 | case FB_EVENT_MODE_CHANGE: |
3234 | fbcon_modechanged(info); | 3234 | fbcon_modechanged(info); |
3235 | break; | 3235 | break; |
3236 | case FB_EVENT_MODE_CHANGE_ALL: | 3236 | case FB_EVENT_MODE_CHANGE_ALL: |
3237 | fbcon_set_all_vcs(info); | 3237 | fbcon_set_all_vcs(info); |
3238 | break; | 3238 | break; |
3239 | case FB_EVENT_MODE_DELETE: | 3239 | case FB_EVENT_MODE_DELETE: |
3240 | mode = event->data; | 3240 | mode = event->data; |
3241 | ret = fbcon_mode_deleted(info, mode); | 3241 | ret = fbcon_mode_deleted(info, mode); |
3242 | break; | 3242 | break; |
3243 | case FB_EVENT_FB_UNBIND: | 3243 | case FB_EVENT_FB_UNBIND: |
3244 | idx = info->node; | 3244 | idx = info->node; |
3245 | ret = fbcon_fb_unbind(idx); | 3245 | ret = fbcon_fb_unbind(idx); |
3246 | break; | 3246 | break; |
3247 | case FB_EVENT_FB_REGISTERED: | 3247 | case FB_EVENT_FB_REGISTERED: |
3248 | ret = fbcon_fb_registered(info); | 3248 | ret = fbcon_fb_registered(info); |
3249 | break; | 3249 | break; |
3250 | case FB_EVENT_FB_UNREGISTERED: | 3250 | case FB_EVENT_FB_UNREGISTERED: |
3251 | ret = fbcon_fb_unregistered(info); | 3251 | ret = fbcon_fb_unregistered(info); |
3252 | break; | 3252 | break; |
3253 | case FB_EVENT_SET_CONSOLE_MAP: | 3253 | case FB_EVENT_SET_CONSOLE_MAP: |
3254 | con2fb = event->data; | 3254 | con2fb = event->data; |
3255 | ret = set_con2fb_map(con2fb->console - 1, | 3255 | ret = set_con2fb_map(con2fb->console - 1, |
3256 | con2fb->framebuffer, 1); | 3256 | con2fb->framebuffer, 1); |
3257 | break; | 3257 | break; |
3258 | case FB_EVENT_GET_CONSOLE_MAP: | 3258 | case FB_EVENT_GET_CONSOLE_MAP: |
3259 | con2fb = event->data; | 3259 | con2fb = event->data; |
3260 | con2fb->framebuffer = con2fb_map[con2fb->console - 1]; | 3260 | con2fb->framebuffer = con2fb_map[con2fb->console - 1]; |
3261 | break; | 3261 | break; |
3262 | case FB_EVENT_BLANK: | 3262 | case FB_EVENT_BLANK: |
3263 | fbcon_fb_blanked(info, *(int *)event->data); | 3263 | fbcon_fb_blanked(info, *(int *)event->data); |
3264 | break; | 3264 | break; |
3265 | case FB_EVENT_NEW_MODELIST: | 3265 | case FB_EVENT_NEW_MODELIST: |
3266 | fbcon_new_modelist(info); | 3266 | fbcon_new_modelist(info); |
3267 | break; | 3267 | break; |
3268 | case FB_EVENT_GET_REQ: | 3268 | case FB_EVENT_GET_REQ: |
3269 | caps = event->data; | 3269 | caps = event->data; |
3270 | fbcon_get_requirement(info, caps); | 3270 | fbcon_get_requirement(info, caps); |
3271 | break; | 3271 | break; |
3272 | case FB_EVENT_REMAP_ALL_CONSOLE: | 3272 | case FB_EVENT_REMAP_ALL_CONSOLE: |
3273 | idx = info->node; | 3273 | idx = info->node; |
3274 | fbcon_remap_all(idx); | 3274 | fbcon_remap_all(idx); |
3275 | break; | 3275 | break; |
3276 | } | 3276 | } |
3277 | done: | 3277 | done: |
3278 | return ret; | 3278 | return ret; |
3279 | } | 3279 | } |
3280 | 3280 | ||
3281 | /* | 3281 | /* |
3282 | * The console `switch' structure for the frame buffer based console | 3282 | * The console `switch' structure for the frame buffer based console |
3283 | */ | 3283 | */ |
3284 | 3284 | ||
3285 | static const struct consw fb_con = { | 3285 | static const struct consw fb_con = { |
3286 | .owner = THIS_MODULE, | 3286 | .owner = THIS_MODULE, |
3287 | .con_startup = fbcon_startup, | 3287 | .con_startup = fbcon_startup, |
3288 | .con_init = fbcon_init, | 3288 | .con_init = fbcon_init, |
3289 | .con_deinit = fbcon_deinit, | 3289 | .con_deinit = fbcon_deinit, |
3290 | .con_clear = fbcon_clear, | 3290 | .con_clear = fbcon_clear, |
3291 | .con_putc = fbcon_putc, | 3291 | .con_putc = fbcon_putc, |
3292 | .con_putcs = fbcon_putcs, | 3292 | .con_putcs = fbcon_putcs, |
3293 | .con_cursor = fbcon_cursor, | 3293 | .con_cursor = fbcon_cursor, |
3294 | .con_scroll = fbcon_scroll, | 3294 | .con_scroll = fbcon_scroll, |
3295 | .con_bmove = fbcon_bmove, | 3295 | .con_bmove = fbcon_bmove, |
3296 | .con_switch = fbcon_switch, | 3296 | .con_switch = fbcon_switch, |
3297 | .con_blank = fbcon_blank, | 3297 | .con_blank = fbcon_blank, |
3298 | .con_font_set = fbcon_set_font, | 3298 | .con_font_set = fbcon_set_font, |
3299 | .con_font_get = fbcon_get_font, | 3299 | .con_font_get = fbcon_get_font, |
3300 | .con_font_default = fbcon_set_def_font, | 3300 | .con_font_default = fbcon_set_def_font, |
3301 | .con_font_copy = fbcon_copy_font, | 3301 | .con_font_copy = fbcon_copy_font, |
3302 | .con_set_palette = fbcon_set_palette, | 3302 | .con_set_palette = fbcon_set_palette, |
3303 | .con_scrolldelta = fbcon_scrolldelta, | 3303 | .con_scrolldelta = fbcon_scrolldelta, |
3304 | .con_set_origin = fbcon_set_origin, | 3304 | .con_set_origin = fbcon_set_origin, |
3305 | .con_invert_region = fbcon_invert_region, | 3305 | .con_invert_region = fbcon_invert_region, |
3306 | .con_screen_pos = fbcon_screen_pos, | 3306 | .con_screen_pos = fbcon_screen_pos, |
3307 | .con_getxy = fbcon_getxy, | 3307 | .con_getxy = fbcon_getxy, |
3308 | .con_resize = fbcon_resize, | 3308 | .con_resize = fbcon_resize, |
3309 | .con_debug_enter = fbcon_debug_enter, | 3309 | .con_debug_enter = fbcon_debug_enter, |
3310 | .con_debug_leave = fbcon_debug_leave, | 3310 | .con_debug_leave = fbcon_debug_leave, |
3311 | }; | 3311 | }; |
3312 | 3312 | ||
3313 | static struct notifier_block fbcon_event_notifier = { | 3313 | static struct notifier_block fbcon_event_notifier = { |
3314 | .notifier_call = fbcon_event_notify, | 3314 | .notifier_call = fbcon_event_notify, |
3315 | }; | 3315 | }; |
3316 | 3316 | ||
3317 | static ssize_t store_rotate(struct device *device, | 3317 | static ssize_t store_rotate(struct device *device, |
3318 | struct device_attribute *attr, const char *buf, | 3318 | struct device_attribute *attr, const char *buf, |
3319 | size_t count) | 3319 | size_t count) |
3320 | { | 3320 | { |
3321 | struct fb_info *info; | 3321 | struct fb_info *info; |
3322 | int rotate, idx; | 3322 | int rotate, idx; |
3323 | char **last = NULL; | 3323 | char **last = NULL; |
3324 | 3324 | ||
3325 | if (fbcon_has_exited) | 3325 | if (fbcon_has_exited) |
3326 | return count; | 3326 | return count; |
3327 | 3327 | ||
3328 | console_lock(); | 3328 | console_lock(); |
3329 | idx = con2fb_map[fg_console]; | 3329 | idx = con2fb_map[fg_console]; |
3330 | 3330 | ||
3331 | if (idx == -1 || registered_fb[idx] == NULL) | 3331 | if (idx == -1 || registered_fb[idx] == NULL) |
3332 | goto err; | 3332 | goto err; |
3333 | 3333 | ||
3334 | info = registered_fb[idx]; | 3334 | info = registered_fb[idx]; |
3335 | rotate = simple_strtoul(buf, last, 0); | 3335 | rotate = simple_strtoul(buf, last, 0); |
3336 | fbcon_rotate(info, rotate); | 3336 | fbcon_rotate(info, rotate); |
3337 | err: | 3337 | err: |
3338 | console_unlock(); | 3338 | console_unlock(); |
3339 | return count; | 3339 | return count; |
3340 | } | 3340 | } |
3341 | 3341 | ||
3342 | static ssize_t store_rotate_all(struct device *device, | 3342 | static ssize_t store_rotate_all(struct device *device, |
3343 | struct device_attribute *attr,const char *buf, | 3343 | struct device_attribute *attr,const char *buf, |
3344 | size_t count) | 3344 | size_t count) |
3345 | { | 3345 | { |
3346 | struct fb_info *info; | 3346 | struct fb_info *info; |
3347 | int rotate, idx; | 3347 | int rotate, idx; |
3348 | char **last = NULL; | 3348 | char **last = NULL; |
3349 | 3349 | ||
3350 | if (fbcon_has_exited) | 3350 | if (fbcon_has_exited) |
3351 | return count; | 3351 | return count; |
3352 | 3352 | ||
3353 | console_lock(); | 3353 | console_lock(); |
3354 | idx = con2fb_map[fg_console]; | 3354 | idx = con2fb_map[fg_console]; |
3355 | 3355 | ||
3356 | if (idx == -1 || registered_fb[idx] == NULL) | 3356 | if (idx == -1 || registered_fb[idx] == NULL) |
3357 | goto err; | 3357 | goto err; |
3358 | 3358 | ||
3359 | info = registered_fb[idx]; | 3359 | info = registered_fb[idx]; |
3360 | rotate = simple_strtoul(buf, last, 0); | 3360 | rotate = simple_strtoul(buf, last, 0); |
3361 | fbcon_rotate_all(info, rotate); | 3361 | fbcon_rotate_all(info, rotate); |
3362 | err: | 3362 | err: |
3363 | console_unlock(); | 3363 | console_unlock(); |
3364 | return count; | 3364 | return count; |
3365 | } | 3365 | } |
3366 | 3366 | ||
3367 | static ssize_t show_rotate(struct device *device, | 3367 | static ssize_t show_rotate(struct device *device, |
3368 | struct device_attribute *attr,char *buf) | 3368 | struct device_attribute *attr,char *buf) |
3369 | { | 3369 | { |
3370 | struct fb_info *info; | 3370 | struct fb_info *info; |
3371 | int rotate = 0, idx; | 3371 | int rotate = 0, idx; |
3372 | 3372 | ||
3373 | if (fbcon_has_exited) | 3373 | if (fbcon_has_exited) |
3374 | return 0; | 3374 | return 0; |
3375 | 3375 | ||
3376 | console_lock(); | 3376 | console_lock(); |
3377 | idx = con2fb_map[fg_console]; | 3377 | idx = con2fb_map[fg_console]; |
3378 | 3378 | ||
3379 | if (idx == -1 || registered_fb[idx] == NULL) | 3379 | if (idx == -1 || registered_fb[idx] == NULL) |
3380 | goto err; | 3380 | goto err; |
3381 | 3381 | ||
3382 | info = registered_fb[idx]; | 3382 | info = registered_fb[idx]; |
3383 | rotate = fbcon_get_rotate(info); | 3383 | rotate = fbcon_get_rotate(info); |
3384 | err: | 3384 | err: |
3385 | console_unlock(); | 3385 | console_unlock(); |
3386 | return snprintf(buf, PAGE_SIZE, "%d\n", rotate); | 3386 | return snprintf(buf, PAGE_SIZE, "%d\n", rotate); |
3387 | } | 3387 | } |
3388 | 3388 | ||
3389 | static ssize_t show_cursor_blink(struct device *device, | 3389 | static ssize_t show_cursor_blink(struct device *device, |
3390 | struct device_attribute *attr, char *buf) | 3390 | struct device_attribute *attr, char *buf) |
3391 | { | 3391 | { |
3392 | struct fb_info *info; | 3392 | struct fb_info *info; |
3393 | struct fbcon_ops *ops; | 3393 | struct fbcon_ops *ops; |
3394 | int idx, blink = -1; | 3394 | int idx, blink = -1; |
3395 | 3395 | ||
3396 | if (fbcon_has_exited) | 3396 | if (fbcon_has_exited) |
3397 | return 0; | 3397 | return 0; |
3398 | 3398 | ||
3399 | console_lock(); | 3399 | console_lock(); |
3400 | idx = con2fb_map[fg_console]; | 3400 | idx = con2fb_map[fg_console]; |
3401 | 3401 | ||
3402 | if (idx == -1 || registered_fb[idx] == NULL) | 3402 | if (idx == -1 || registered_fb[idx] == NULL) |
3403 | goto err; | 3403 | goto err; |
3404 | 3404 | ||
3405 | info = registered_fb[idx]; | 3405 | info = registered_fb[idx]; |
3406 | ops = info->fbcon_par; | 3406 | ops = info->fbcon_par; |
3407 | 3407 | ||
3408 | if (!ops) | 3408 | if (!ops) |
3409 | goto err; | 3409 | goto err; |
3410 | 3410 | ||
3411 | blink = (ops->flags & FBCON_FLAGS_CURSOR_TIMER) ? 1 : 0; | 3411 | blink = (ops->flags & FBCON_FLAGS_CURSOR_TIMER) ? 1 : 0; |
3412 | err: | 3412 | err: |
3413 | console_unlock(); | 3413 | console_unlock(); |
3414 | return snprintf(buf, PAGE_SIZE, "%d\n", blink); | 3414 | return snprintf(buf, PAGE_SIZE, "%d\n", blink); |
3415 | } | 3415 | } |
3416 | 3416 | ||
3417 | static ssize_t store_cursor_blink(struct device *device, | 3417 | static ssize_t store_cursor_blink(struct device *device, |
3418 | struct device_attribute *attr, | 3418 | struct device_attribute *attr, |
3419 | const char *buf, size_t count) | 3419 | const char *buf, size_t count) |
3420 | { | 3420 | { |
3421 | struct fb_info *info; | 3421 | struct fb_info *info; |
3422 | int blink, idx; | 3422 | int blink, idx; |
3423 | char **last = NULL; | 3423 | char **last = NULL; |
3424 | 3424 | ||
3425 | if (fbcon_has_exited) | 3425 | if (fbcon_has_exited) |
3426 | return count; | 3426 | return count; |
3427 | 3427 | ||
3428 | console_lock(); | 3428 | console_lock(); |
3429 | idx = con2fb_map[fg_console]; | 3429 | idx = con2fb_map[fg_console]; |
3430 | 3430 | ||
3431 | if (idx == -1 || registered_fb[idx] == NULL) | 3431 | if (idx == -1 || registered_fb[idx] == NULL) |
3432 | goto err; | 3432 | goto err; |
3433 | 3433 | ||
3434 | info = registered_fb[idx]; | 3434 | info = registered_fb[idx]; |
3435 | 3435 | ||
3436 | if (!info->fbcon_par) | 3436 | if (!info->fbcon_par) |
3437 | goto err; | 3437 | goto err; |
3438 | 3438 | ||
3439 | blink = simple_strtoul(buf, last, 0); | 3439 | blink = simple_strtoul(buf, last, 0); |
3440 | 3440 | ||
3441 | if (blink) { | 3441 | if (blink) { |
3442 | fbcon_cursor_noblink = 0; | 3442 | fbcon_cursor_noblink = 0; |
3443 | fbcon_add_cursor_timer(info); | 3443 | fbcon_add_cursor_timer(info); |
3444 | } else { | 3444 | } else { |
3445 | fbcon_cursor_noblink = 1; | 3445 | fbcon_cursor_noblink = 1; |
3446 | fbcon_del_cursor_timer(info); | 3446 | fbcon_del_cursor_timer(info); |
3447 | } | 3447 | } |
3448 | 3448 | ||
3449 | err: | 3449 | err: |
3450 | console_unlock(); | 3450 | console_unlock(); |
3451 | return count; | 3451 | return count; |
3452 | } | 3452 | } |
3453 | 3453 | ||
3454 | static struct device_attribute device_attrs[] = { | 3454 | static struct device_attribute device_attrs[] = { |
3455 | __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate), | 3455 | __ATTR(rotate, S_IRUGO|S_IWUSR, show_rotate, store_rotate), |
3456 | __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all), | 3456 | __ATTR(rotate_all, S_IWUSR, NULL, store_rotate_all), |
3457 | __ATTR(cursor_blink, S_IRUGO|S_IWUSR, show_cursor_blink, | 3457 | __ATTR(cursor_blink, S_IRUGO|S_IWUSR, show_cursor_blink, |
3458 | store_cursor_blink), | 3458 | store_cursor_blink), |
3459 | }; | 3459 | }; |
3460 | 3460 | ||
3461 | static int fbcon_init_device(void) | 3461 | static int fbcon_init_device(void) |
3462 | { | 3462 | { |
3463 | int i, error = 0; | 3463 | int i, error = 0; |
3464 | 3464 | ||
3465 | fbcon_has_sysfs = 1; | 3465 | fbcon_has_sysfs = 1; |
3466 | 3466 | ||
3467 | for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { | 3467 | for (i = 0; i < ARRAY_SIZE(device_attrs); i++) { |
3468 | error = device_create_file(fbcon_device, &device_attrs[i]); | 3468 | error = device_create_file(fbcon_device, &device_attrs[i]); |
3469 | 3469 | ||
3470 | if (error) | 3470 | if (error) |
3471 | break; | 3471 | break; |
3472 | } | 3472 | } |
3473 | 3473 | ||
3474 | if (error) { | 3474 | if (error) { |
3475 | while (--i >= 0) | 3475 | while (--i >= 0) |
3476 | device_remove_file(fbcon_device, &device_attrs[i]); | 3476 | device_remove_file(fbcon_device, &device_attrs[i]); |
3477 | 3477 | ||
3478 | fbcon_has_sysfs = 0; | 3478 | fbcon_has_sysfs = 0; |
3479 | } | 3479 | } |
3480 | 3480 | ||
3481 | return 0; | 3481 | return 0; |
3482 | } | 3482 | } |
3483 | 3483 | ||
3484 | static void fbcon_start(void) | 3484 | static void fbcon_start(void) |
3485 | { | 3485 | { |
3486 | if (num_registered_fb) { | 3486 | if (num_registered_fb) { |
3487 | int i; | 3487 | int i; |
3488 | 3488 | ||
3489 | console_lock(); | 3489 | console_lock(); |
3490 | 3490 | ||
3491 | for (i = 0; i < FB_MAX; i++) { | 3491 | for (i = 0; i < FB_MAX; i++) { |
3492 | if (registered_fb[i] != NULL) { | 3492 | if (registered_fb[i] != NULL) { |
3493 | info_idx = i; | 3493 | info_idx = i; |
3494 | break; | 3494 | break; |
3495 | } | 3495 | } |
3496 | } | 3496 | } |
3497 | 3497 | ||
3498 | console_unlock(); | 3498 | console_unlock(); |
3499 | fbcon_takeover(0); | 3499 | fbcon_takeover(0); |
3500 | } | 3500 | } |
3501 | } | 3501 | } |
3502 | 3502 | ||
3503 | static void fbcon_exit(void) | 3503 | static void fbcon_exit(void) |
3504 | { | 3504 | { |
3505 | struct fb_info *info; | 3505 | struct fb_info *info; |
3506 | int i, j, mapped; | 3506 | int i, j, mapped; |
3507 | 3507 | ||
3508 | if (fbcon_has_exited) | 3508 | if (fbcon_has_exited) |
3509 | return; | 3509 | return; |
3510 | 3510 | ||
3511 | kfree((void *)softback_buf); | 3511 | kfree((void *)softback_buf); |
3512 | softback_buf = 0UL; | 3512 | softback_buf = 0UL; |
3513 | 3513 | ||
3514 | for (i = 0; i < FB_MAX; i++) { | 3514 | for (i = 0; i < FB_MAX; i++) { |
3515 | int pending = 0; | 3515 | int pending = 0; |
3516 | 3516 | ||
3517 | mapped = 0; | 3517 | mapped = 0; |
3518 | info = registered_fb[i]; | 3518 | info = registered_fb[i]; |
3519 | 3519 | ||
3520 | if (info == NULL) | 3520 | if (info == NULL) |
3521 | continue; | 3521 | continue; |
3522 | 3522 | ||
3523 | if (info->queue.func) | 3523 | if (info->queue.func) |
3524 | pending = cancel_work_sync(&info->queue); | 3524 | pending = cancel_work_sync(&info->queue); |
3525 | DPRINTK("fbcon: %s pending work\n", (pending ? "canceled" : | 3525 | DPRINTK("fbcon: %s pending work\n", (pending ? "canceled" : |
3526 | "no")); | 3526 | "no")); |
3527 | 3527 | ||
3528 | for (j = first_fb_vc; j <= last_fb_vc; j++) { | 3528 | for (j = first_fb_vc; j <= last_fb_vc; j++) { |
3529 | if (con2fb_map[j] == i) | 3529 | if (con2fb_map[j] == i) |
3530 | mapped = 1; | 3530 | mapped = 1; |
3531 | } | 3531 | } |
3532 | 3532 | ||
3533 | if (mapped) { | 3533 | if (mapped) { |
3534 | if (info->fbops->fb_release) | 3534 | if (info->fbops->fb_release) |
3535 | info->fbops->fb_release(info, 0); | 3535 | info->fbops->fb_release(info, 0); |
3536 | module_put(info->fbops->owner); | 3536 | module_put(info->fbops->owner); |
3537 | 3537 | ||
3538 | if (info->fbcon_par) { | 3538 | if (info->fbcon_par) { |
3539 | struct fbcon_ops *ops = info->fbcon_par; | 3539 | struct fbcon_ops *ops = info->fbcon_par; |
3540 | 3540 | ||
3541 | fbcon_del_cursor_timer(info); | 3541 | fbcon_del_cursor_timer(info); |
3542 | kfree(ops->cursor_src); | 3542 | kfree(ops->cursor_src); |
3543 | kfree(info->fbcon_par); | 3543 | kfree(info->fbcon_par); |
3544 | info->fbcon_par = NULL; | 3544 | info->fbcon_par = NULL; |
3545 | } | 3545 | } |
3546 | 3546 | ||
3547 | if (info->queue.func == fb_flashcursor) | 3547 | if (info->queue.func == fb_flashcursor) |
3548 | info->queue.func = NULL; | 3548 | info->queue.func = NULL; |
3549 | } | 3549 | } |
3550 | } | 3550 | } |
3551 | 3551 | ||
3552 | fbcon_has_exited = 1; | 3552 | fbcon_has_exited = 1; |
3553 | } | 3553 | } |
3554 | 3554 | ||
3555 | static int __init fb_console_init(void) | 3555 | static int __init fb_console_init(void) |
3556 | { | 3556 | { |
3557 | int i; | 3557 | int i; |
3558 | 3558 | ||
3559 | console_lock(); | 3559 | console_lock(); |
3560 | fb_register_client(&fbcon_event_notifier); | 3560 | fb_register_client(&fbcon_event_notifier); |
3561 | fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL, | 3561 | fbcon_device = device_create(fb_class, NULL, MKDEV(0, 0), NULL, |
3562 | "fbcon"); | 3562 | "fbcon"); |
3563 | 3563 | ||
3564 | if (IS_ERR(fbcon_device)) { | 3564 | if (IS_ERR(fbcon_device)) { |
3565 | printk(KERN_WARNING "Unable to create device " | 3565 | printk(KERN_WARNING "Unable to create device " |
3566 | "for fbcon; errno = %ld\n", | 3566 | "for fbcon; errno = %ld\n", |
3567 | PTR_ERR(fbcon_device)); | 3567 | PTR_ERR(fbcon_device)); |
3568 | fbcon_device = NULL; | 3568 | fbcon_device = NULL; |
3569 | } else | 3569 | } else |
3570 | fbcon_init_device(); | 3570 | fbcon_init_device(); |
3571 | 3571 | ||
3572 | for (i = 0; i < MAX_NR_CONSOLES; i++) | 3572 | for (i = 0; i < MAX_NR_CONSOLES; i++) |
3573 | con2fb_map[i] = -1; | 3573 | con2fb_map[i] = -1; |
3574 | 3574 | ||
3575 | console_unlock(); | 3575 | console_unlock(); |
3576 | fbcon_start(); | 3576 | fbcon_start(); |
3577 | return 0; | 3577 | return 0; |
3578 | } | 3578 | } |
3579 | 3579 | ||
3580 | module_init(fb_console_init); | 3580 | module_init(fb_console_init); |
3581 | 3581 | ||
3582 | #ifdef MODULE | 3582 | #ifdef MODULE |
3583 | 3583 | ||
3584 | static void __exit fbcon_deinit_device(void) | 3584 | static void __exit fbcon_deinit_device(void) |
3585 | { | 3585 | { |
3586 | int i; | 3586 | int i; |
3587 | 3587 | ||
3588 | if (fbcon_has_sysfs) { | 3588 | if (fbcon_has_sysfs) { |
3589 | for (i = 0; i < ARRAY_SIZE(device_attrs); i++) | 3589 | for (i = 0; i < ARRAY_SIZE(device_attrs); i++) |
3590 | device_remove_file(fbcon_device, &device_attrs[i]); | 3590 | device_remove_file(fbcon_device, &device_attrs[i]); |
3591 | 3591 | ||
3592 | fbcon_has_sysfs = 0; | 3592 | fbcon_has_sysfs = 0; |
3593 | } | 3593 | } |
3594 | } | 3594 | } |
3595 | 3595 | ||
3596 | static void __exit fb_console_exit(void) | 3596 | static void __exit fb_console_exit(void) |
3597 | { | 3597 | { |
3598 | console_lock(); | 3598 | console_lock(); |
3599 | fb_unregister_client(&fbcon_event_notifier); | 3599 | fb_unregister_client(&fbcon_event_notifier); |
3600 | fbcon_deinit_device(); | 3600 | fbcon_deinit_device(); |
3601 | device_destroy(fb_class, MKDEV(0, 0)); | 3601 | device_destroy(fb_class, MKDEV(0, 0)); |
3602 | fbcon_exit(); | 3602 | fbcon_exit(); |
3603 | console_unlock(); | 3603 | console_unlock(); |
3604 | unregister_con_driver(&fb_con); | 3604 | unregister_con_driver(&fb_con); |
3605 | } | 3605 | } |
3606 | 3606 | ||
3607 | module_exit(fb_console_exit); | 3607 | module_exit(fb_console_exit); |
3608 | 3608 | ||
3609 | #endif | 3609 | #endif |
3610 | 3610 | ||
3611 | MODULE_LICENSE("GPL"); | 3611 | MODULE_LICENSE("GPL"); |
3612 | 3612 |
drivers/video/mb862xx/mb862xxfbdrv.c
1 | /* | 1 | /* |
2 | * drivers/mb862xx/mb862xxfb.c | 2 | * drivers/mb862xx/mb862xxfb.c |
3 | * | 3 | * |
4 | * Fujitsu Carmine/Coral-P(A)/Lime framebuffer driver | 4 | * Fujitsu Carmine/Coral-P(A)/Lime framebuffer driver |
5 | * | 5 | * |
6 | * (C) 2008 Anatolij Gustschin <agust@denx.de> | 6 | * (C) 2008 Anatolij Gustschin <agust@denx.de> |
7 | * DENX Software Engineering | 7 | * DENX Software Engineering |
8 | * | 8 | * |
9 | * This program is free software; you can redistribute it and/or modify | 9 | * This program is free software; you can redistribute it and/or modify |
10 | * it under the terms of the GNU General Public License version 2 as | 10 | * it under the terms of the GNU General Public License version 2 as |
11 | * published by the Free Software Foundation. | 11 | * published by the Free Software Foundation. |
12 | * | 12 | * |
13 | */ | 13 | */ |
14 | 14 | ||
15 | #undef DEBUG | 15 | #undef DEBUG |
16 | 16 | ||
17 | #include <linux/fb.h> | 17 | #include <linux/fb.h> |
18 | #include <linux/delay.h> | 18 | #include <linux/delay.h> |
19 | #include <linux/uaccess.h> | 19 | #include <linux/uaccess.h> |
20 | #include <linux/module.h> | 20 | #include <linux/module.h> |
21 | #include <linux/init.h> | 21 | #include <linux/init.h> |
22 | #include <linux/interrupt.h> | 22 | #include <linux/interrupt.h> |
23 | #include <linux/pci.h> | 23 | #include <linux/pci.h> |
24 | #if defined(CONFIG_OF) | 24 | #if defined(CONFIG_OF) |
25 | #include <linux/of_platform.h> | 25 | #include <linux/of_platform.h> |
26 | #endif | 26 | #endif |
27 | #include "mb862xxfb.h" | 27 | #include "mb862xxfb.h" |
28 | #include "mb862xx_reg.h" | 28 | #include "mb862xx_reg.h" |
29 | 29 | ||
30 | #define NR_PALETTE 256 | 30 | #define NR_PALETTE 256 |
31 | #define MB862XX_MEM_SIZE 0x1000000 | 31 | #define MB862XX_MEM_SIZE 0x1000000 |
32 | #define CORALP_MEM_SIZE 0x2000000 | 32 | #define CORALP_MEM_SIZE 0x2000000 |
33 | #define CARMINE_MEM_SIZE 0x8000000 | 33 | #define CARMINE_MEM_SIZE 0x8000000 |
34 | #define DRV_NAME "mb862xxfb" | 34 | #define DRV_NAME "mb862xxfb" |
35 | 35 | ||
36 | #if defined(CONFIG_SOCRATES) | 36 | #if defined(CONFIG_SOCRATES) |
37 | static struct mb862xx_gc_mode socrates_gc_mode = { | 37 | static struct mb862xx_gc_mode socrates_gc_mode = { |
38 | /* Mode for Prime View PM070WL4 TFT LCD Panel */ | 38 | /* Mode for Prime View PM070WL4 TFT LCD Panel */ |
39 | { "800x480", 45, 800, 480, 40000, 86, 42, 33, 10, 128, 2, 0, 0, 0 }, | 39 | { "800x480", 45, 800, 480, 40000, 86, 42, 33, 10, 128, 2, 0, 0, 0 }, |
40 | /* 16 bits/pixel, 16MB, 133MHz, SDRAM memory mode value */ | 40 | /* 16 bits/pixel, 16MB, 133MHz, SDRAM memory mode value */ |
41 | 16, 0x1000000, GC_CCF_COT_133, 0x4157ba63 | 41 | 16, 0x1000000, GC_CCF_COT_133, 0x4157ba63 |
42 | }; | 42 | }; |
43 | #endif | 43 | #endif |
44 | 44 | ||
45 | /* Helpers */ | 45 | /* Helpers */ |
46 | static inline int h_total(struct fb_var_screeninfo *var) | 46 | static inline int h_total(struct fb_var_screeninfo *var) |
47 | { | 47 | { |
48 | return var->xres + var->left_margin + | 48 | return var->xres + var->left_margin + |
49 | var->right_margin + var->hsync_len; | 49 | var->right_margin + var->hsync_len; |
50 | } | 50 | } |
51 | 51 | ||
52 | static inline int v_total(struct fb_var_screeninfo *var) | 52 | static inline int v_total(struct fb_var_screeninfo *var) |
53 | { | 53 | { |
54 | return var->yres + var->upper_margin + | 54 | return var->yres + var->upper_margin + |
55 | var->lower_margin + var->vsync_len; | 55 | var->lower_margin + var->vsync_len; |
56 | } | 56 | } |
57 | 57 | ||
58 | static inline int hsp(struct fb_var_screeninfo *var) | 58 | static inline int hsp(struct fb_var_screeninfo *var) |
59 | { | 59 | { |
60 | return var->xres + var->right_margin - 1; | 60 | return var->xres + var->right_margin - 1; |
61 | } | 61 | } |
62 | 62 | ||
63 | static inline int vsp(struct fb_var_screeninfo *var) | 63 | static inline int vsp(struct fb_var_screeninfo *var) |
64 | { | 64 | { |
65 | return var->yres + var->lower_margin - 1; | 65 | return var->yres + var->lower_margin - 1; |
66 | } | 66 | } |
67 | 67 | ||
68 | static inline int d_pitch(struct fb_var_screeninfo *var) | 68 | static inline int d_pitch(struct fb_var_screeninfo *var) |
69 | { | 69 | { |
70 | return var->xres * var->bits_per_pixel / 8; | 70 | return var->xres * var->bits_per_pixel / 8; |
71 | } | 71 | } |
72 | 72 | ||
73 | static inline unsigned int chan_to_field(unsigned int chan, | 73 | static inline unsigned int chan_to_field(unsigned int chan, |
74 | struct fb_bitfield *bf) | 74 | struct fb_bitfield *bf) |
75 | { | 75 | { |
76 | chan &= 0xffff; | 76 | chan &= 0xffff; |
77 | chan >>= 16 - bf->length; | 77 | chan >>= 16 - bf->length; |
78 | return chan << bf->offset; | 78 | return chan << bf->offset; |
79 | } | 79 | } |
80 | 80 | ||
81 | static int mb862xxfb_setcolreg(unsigned regno, | 81 | static int mb862xxfb_setcolreg(unsigned regno, |
82 | unsigned red, unsigned green, unsigned blue, | 82 | unsigned red, unsigned green, unsigned blue, |
83 | unsigned transp, struct fb_info *info) | 83 | unsigned transp, struct fb_info *info) |
84 | { | 84 | { |
85 | struct mb862xxfb_par *par = info->par; | 85 | struct mb862xxfb_par *par = info->par; |
86 | unsigned int val; | 86 | unsigned int val; |
87 | 87 | ||
88 | switch (info->fix.visual) { | 88 | switch (info->fix.visual) { |
89 | case FB_VISUAL_TRUECOLOR: | 89 | case FB_VISUAL_TRUECOLOR: |
90 | if (regno < 16) { | 90 | if (regno < 16) { |
91 | val = chan_to_field(red, &info->var.red); | 91 | val = chan_to_field(red, &info->var.red); |
92 | val |= chan_to_field(green, &info->var.green); | 92 | val |= chan_to_field(green, &info->var.green); |
93 | val |= chan_to_field(blue, &info->var.blue); | 93 | val |= chan_to_field(blue, &info->var.blue); |
94 | par->pseudo_palette[regno] = val; | 94 | par->pseudo_palette[regno] = val; |
95 | } | 95 | } |
96 | break; | 96 | break; |
97 | case FB_VISUAL_PSEUDOCOLOR: | 97 | case FB_VISUAL_PSEUDOCOLOR: |
98 | if (regno < 256) { | 98 | if (regno < 256) { |
99 | val = (red >> 8) << 16; | 99 | val = (red >> 8) << 16; |
100 | val |= (green >> 8) << 8; | 100 | val |= (green >> 8) << 8; |
101 | val |= blue >> 8; | 101 | val |= blue >> 8; |
102 | outreg(disp, GC_L0PAL0 + (regno * 4), val); | 102 | outreg(disp, GC_L0PAL0 + (regno * 4), val); |
103 | } | 103 | } |
104 | break; | 104 | break; |
105 | default: | 105 | default: |
106 | return 1; /* unsupported type */ | 106 | return 1; /* unsupported type */ |
107 | } | 107 | } |
108 | return 0; | 108 | return 0; |
109 | } | 109 | } |
110 | 110 | ||
111 | static int mb862xxfb_check_var(struct fb_var_screeninfo *var, | 111 | static int mb862xxfb_check_var(struct fb_var_screeninfo *var, |
112 | struct fb_info *fbi) | 112 | struct fb_info *fbi) |
113 | { | 113 | { |
114 | unsigned long tmp; | 114 | unsigned long tmp; |
115 | 115 | ||
116 | if (fbi->dev) | 116 | if (fbi->dev) |
117 | dev_dbg(fbi->dev, "%s\n", __func__); | 117 | dev_dbg(fbi->dev, "%s\n", __func__); |
118 | 118 | ||
119 | /* check if these values fit into the registers */ | 119 | /* check if these values fit into the registers */ |
120 | if (var->hsync_len > 255 || var->vsync_len > 255) | 120 | if (var->hsync_len > 255 || var->vsync_len > 255) |
121 | return -EINVAL; | 121 | return -EINVAL; |
122 | 122 | ||
123 | if ((var->xres + var->right_margin) >= 4096) | 123 | if ((var->xres + var->right_margin) >= 4096) |
124 | return -EINVAL; | 124 | return -EINVAL; |
125 | 125 | ||
126 | if ((var->yres + var->lower_margin) > 4096) | 126 | if ((var->yres + var->lower_margin) > 4096) |
127 | return -EINVAL; | 127 | return -EINVAL; |
128 | 128 | ||
129 | if (h_total(var) > 4096 || v_total(var) > 4096) | 129 | if (h_total(var) > 4096 || v_total(var) > 4096) |
130 | return -EINVAL; | 130 | return -EINVAL; |
131 | 131 | ||
132 | if (var->xres_virtual > 4096 || var->yres_virtual > 4096) | 132 | if (var->xres_virtual > 4096 || var->yres_virtual > 4096) |
133 | return -EINVAL; | 133 | return -EINVAL; |
134 | 134 | ||
135 | if (var->bits_per_pixel <= 8) | 135 | if (var->bits_per_pixel <= 8) |
136 | var->bits_per_pixel = 8; | 136 | var->bits_per_pixel = 8; |
137 | else if (var->bits_per_pixel <= 16) | 137 | else if (var->bits_per_pixel <= 16) |
138 | var->bits_per_pixel = 16; | 138 | var->bits_per_pixel = 16; |
139 | else if (var->bits_per_pixel <= 32) | 139 | else if (var->bits_per_pixel <= 32) |
140 | var->bits_per_pixel = 32; | 140 | var->bits_per_pixel = 32; |
141 | 141 | ||
142 | /* | 142 | /* |
143 | * can cope with 8,16 or 24/32bpp if resulting | 143 | * can cope with 8,16 or 24/32bpp if resulting |
144 | * pitch is divisible by 64 without remainder | 144 | * pitch is divisible by 64 without remainder |
145 | */ | 145 | */ |
146 | if (d_pitch(&fbi->var) % GC_L0M_L0W_UNIT) { | 146 | if (d_pitch(&fbi->var) % GC_L0M_L0W_UNIT) { |
147 | int r; | 147 | int r; |
148 | 148 | ||
149 | var->bits_per_pixel = 0; | 149 | var->bits_per_pixel = 0; |
150 | do { | 150 | do { |
151 | var->bits_per_pixel += 8; | 151 | var->bits_per_pixel += 8; |
152 | r = d_pitch(&fbi->var) % GC_L0M_L0W_UNIT; | 152 | r = d_pitch(&fbi->var) % GC_L0M_L0W_UNIT; |
153 | } while (r && var->bits_per_pixel <= 32); | 153 | } while (r && var->bits_per_pixel <= 32); |
154 | 154 | ||
155 | if (d_pitch(&fbi->var) % GC_L0M_L0W_UNIT) | 155 | if (d_pitch(&fbi->var) % GC_L0M_L0W_UNIT) |
156 | return -EINVAL; | 156 | return -EINVAL; |
157 | } | 157 | } |
158 | 158 | ||
159 | /* line length is going to be 128 bit aligned */ | 159 | /* line length is going to be 128 bit aligned */ |
160 | tmp = (var->xres * var->bits_per_pixel) / 8; | 160 | tmp = (var->xres * var->bits_per_pixel) / 8; |
161 | if ((tmp & 15) != 0) | 161 | if ((tmp & 15) != 0) |
162 | return -EINVAL; | 162 | return -EINVAL; |
163 | 163 | ||
164 | /* set r/g/b positions and validate bpp */ | 164 | /* set r/g/b positions and validate bpp */ |
165 | switch (var->bits_per_pixel) { | 165 | switch (var->bits_per_pixel) { |
166 | case 8: | 166 | case 8: |
167 | var->red.length = var->bits_per_pixel; | 167 | var->red.length = var->bits_per_pixel; |
168 | var->green.length = var->bits_per_pixel; | 168 | var->green.length = var->bits_per_pixel; |
169 | var->blue.length = var->bits_per_pixel; | 169 | var->blue.length = var->bits_per_pixel; |
170 | var->red.offset = 0; | 170 | var->red.offset = 0; |
171 | var->green.offset = 0; | 171 | var->green.offset = 0; |
172 | var->blue.offset = 0; | 172 | var->blue.offset = 0; |
173 | var->transp.length = 0; | 173 | var->transp.length = 0; |
174 | break; | 174 | break; |
175 | case 16: | 175 | case 16: |
176 | var->red.length = 5; | 176 | var->red.length = 5; |
177 | var->green.length = 5; | 177 | var->green.length = 5; |
178 | var->blue.length = 5; | 178 | var->blue.length = 5; |
179 | var->red.offset = 10; | 179 | var->red.offset = 10; |
180 | var->green.offset = 5; | 180 | var->green.offset = 5; |
181 | var->blue.offset = 0; | 181 | var->blue.offset = 0; |
182 | var->transp.length = 0; | 182 | var->transp.length = 0; |
183 | break; | 183 | break; |
184 | case 24: | 184 | case 24: |
185 | case 32: | 185 | case 32: |
186 | var->transp.length = 8; | 186 | var->transp.length = 8; |
187 | var->red.length = 8; | 187 | var->red.length = 8; |
188 | var->green.length = 8; | 188 | var->green.length = 8; |
189 | var->blue.length = 8; | 189 | var->blue.length = 8; |
190 | var->transp.offset = 24; | 190 | var->transp.offset = 24; |
191 | var->red.offset = 16; | 191 | var->red.offset = 16; |
192 | var->green.offset = 8; | 192 | var->green.offset = 8; |
193 | var->blue.offset = 0; | 193 | var->blue.offset = 0; |
194 | break; | 194 | break; |
195 | default: | 195 | default: |
196 | return -EINVAL; | 196 | return -EINVAL; |
197 | } | 197 | } |
198 | return 0; | 198 | return 0; |
199 | } | 199 | } |
200 | 200 | ||
201 | /* | 201 | /* |
202 | * set display parameters | 202 | * set display parameters |
203 | */ | 203 | */ |
204 | static int mb862xxfb_set_par(struct fb_info *fbi) | 204 | static int mb862xxfb_set_par(struct fb_info *fbi) |
205 | { | 205 | { |
206 | struct mb862xxfb_par *par = fbi->par; | 206 | struct mb862xxfb_par *par = fbi->par; |
207 | unsigned long reg, sc; | 207 | unsigned long reg, sc; |
208 | 208 | ||
209 | dev_dbg(par->dev, "%s\n", __func__); | 209 | dev_dbg(par->dev, "%s\n", __func__); |
210 | if (par->type == BT_CORALP) | 210 | if (par->type == BT_CORALP) |
211 | mb862xxfb_init_accel(fbi, fbi->var.xres); | 211 | mb862xxfb_init_accel(fbi, fbi->var.xres); |
212 | 212 | ||
213 | if (par->pre_init) | 213 | if (par->pre_init) |
214 | return 0; | 214 | return 0; |
215 | 215 | ||
216 | /* disp off */ | 216 | /* disp off */ |
217 | reg = inreg(disp, GC_DCM1); | 217 | reg = inreg(disp, GC_DCM1); |
218 | reg &= ~GC_DCM01_DEN; | 218 | reg &= ~GC_DCM01_DEN; |
219 | outreg(disp, GC_DCM1, reg); | 219 | outreg(disp, GC_DCM1, reg); |
220 | 220 | ||
221 | /* set display reference clock div. */ | 221 | /* set display reference clock div. */ |
222 | sc = par->refclk / (1000000 / fbi->var.pixclock) - 1; | 222 | sc = par->refclk / (1000000 / fbi->var.pixclock) - 1; |
223 | reg = inreg(disp, GC_DCM1); | 223 | reg = inreg(disp, GC_DCM1); |
224 | reg &= ~(GC_DCM01_CKS | GC_DCM01_RESV | GC_DCM01_SC); | 224 | reg &= ~(GC_DCM01_CKS | GC_DCM01_RESV | GC_DCM01_SC); |
225 | reg |= sc << 8; | 225 | reg |= sc << 8; |
226 | outreg(disp, GC_DCM1, reg); | 226 | outreg(disp, GC_DCM1, reg); |
227 | dev_dbg(par->dev, "SC 0x%lx\n", sc); | 227 | dev_dbg(par->dev, "SC 0x%lx\n", sc); |
228 | 228 | ||
229 | /* disp dimension, format */ | 229 | /* disp dimension, format */ |
230 | reg = pack(d_pitch(&fbi->var) / GC_L0M_L0W_UNIT, | 230 | reg = pack(d_pitch(&fbi->var) / GC_L0M_L0W_UNIT, |
231 | (fbi->var.yres - 1)); | 231 | (fbi->var.yres - 1)); |
232 | if (fbi->var.bits_per_pixel == 16) | 232 | if (fbi->var.bits_per_pixel == 16) |
233 | reg |= GC_L0M_L0C_16; | 233 | reg |= GC_L0M_L0C_16; |
234 | outreg(disp, GC_L0M, reg); | 234 | outreg(disp, GC_L0M, reg); |
235 | 235 | ||
236 | if (fbi->var.bits_per_pixel == 32) { | 236 | if (fbi->var.bits_per_pixel == 32) { |
237 | reg = inreg(disp, GC_L0EM); | 237 | reg = inreg(disp, GC_L0EM); |
238 | outreg(disp, GC_L0EM, reg | GC_L0EM_L0EC_24); | 238 | outreg(disp, GC_L0EM, reg | GC_L0EM_L0EC_24); |
239 | } | 239 | } |
240 | outreg(disp, GC_WY_WX, 0); | 240 | outreg(disp, GC_WY_WX, 0); |
241 | reg = pack(fbi->var.yres - 1, fbi->var.xres); | 241 | reg = pack(fbi->var.yres - 1, fbi->var.xres); |
242 | outreg(disp, GC_WH_WW, reg); | 242 | outreg(disp, GC_WH_WW, reg); |
243 | outreg(disp, GC_L0OA0, 0); | 243 | outreg(disp, GC_L0OA0, 0); |
244 | outreg(disp, GC_L0DA0, 0); | 244 | outreg(disp, GC_L0DA0, 0); |
245 | outreg(disp, GC_L0DY_L0DX, 0); | 245 | outreg(disp, GC_L0DY_L0DX, 0); |
246 | outreg(disp, GC_L0WY_L0WX, 0); | 246 | outreg(disp, GC_L0WY_L0WX, 0); |
247 | outreg(disp, GC_L0WH_L0WW, reg); | 247 | outreg(disp, GC_L0WH_L0WW, reg); |
248 | 248 | ||
249 | /* both HW-cursors off */ | 249 | /* both HW-cursors off */ |
250 | reg = inreg(disp, GC_CPM_CUTC); | 250 | reg = inreg(disp, GC_CPM_CUTC); |
251 | reg &= ~(GC_CPM_CEN0 | GC_CPM_CEN1); | 251 | reg &= ~(GC_CPM_CEN0 | GC_CPM_CEN1); |
252 | outreg(disp, GC_CPM_CUTC, reg); | 252 | outreg(disp, GC_CPM_CUTC, reg); |
253 | 253 | ||
254 | /* timings */ | 254 | /* timings */ |
255 | reg = pack(fbi->var.xres - 1, fbi->var.xres - 1); | 255 | reg = pack(fbi->var.xres - 1, fbi->var.xres - 1); |
256 | outreg(disp, GC_HDB_HDP, reg); | 256 | outreg(disp, GC_HDB_HDP, reg); |
257 | reg = pack((fbi->var.yres - 1), vsp(&fbi->var)); | 257 | reg = pack((fbi->var.yres - 1), vsp(&fbi->var)); |
258 | outreg(disp, GC_VDP_VSP, reg); | 258 | outreg(disp, GC_VDP_VSP, reg); |
259 | reg = ((fbi->var.vsync_len - 1) << 24) | | 259 | reg = ((fbi->var.vsync_len - 1) << 24) | |
260 | pack((fbi->var.hsync_len - 1), hsp(&fbi->var)); | 260 | pack((fbi->var.hsync_len - 1), hsp(&fbi->var)); |
261 | outreg(disp, GC_VSW_HSW_HSP, reg); | 261 | outreg(disp, GC_VSW_HSW_HSP, reg); |
262 | outreg(disp, GC_HTP, pack(h_total(&fbi->var) - 1, 0)); | 262 | outreg(disp, GC_HTP, pack(h_total(&fbi->var) - 1, 0)); |
263 | outreg(disp, GC_VTR, pack(v_total(&fbi->var) - 1, 0)); | 263 | outreg(disp, GC_VTR, pack(v_total(&fbi->var) - 1, 0)); |
264 | 264 | ||
265 | /* display on */ | 265 | /* display on */ |
266 | reg = inreg(disp, GC_DCM1); | 266 | reg = inreg(disp, GC_DCM1); |
267 | reg |= GC_DCM01_DEN | GC_DCM01_L0E; | 267 | reg |= GC_DCM01_DEN | GC_DCM01_L0E; |
268 | reg &= ~GC_DCM01_ESY; | 268 | reg &= ~GC_DCM01_ESY; |
269 | outreg(disp, GC_DCM1, reg); | 269 | outreg(disp, GC_DCM1, reg); |
270 | return 0; | 270 | return 0; |
271 | } | 271 | } |
272 | 272 | ||
273 | static int mb862xxfb_pan(struct fb_var_screeninfo *var, | 273 | static int mb862xxfb_pan(struct fb_var_screeninfo *var, |
274 | struct fb_info *info) | 274 | struct fb_info *info) |
275 | { | 275 | { |
276 | struct mb862xxfb_par *par = info->par; | 276 | struct mb862xxfb_par *par = info->par; |
277 | unsigned long reg; | 277 | unsigned long reg; |
278 | 278 | ||
279 | reg = pack(var->yoffset, var->xoffset); | 279 | reg = pack(var->yoffset, var->xoffset); |
280 | outreg(disp, GC_L0WY_L0WX, reg); | 280 | outreg(disp, GC_L0WY_L0WX, reg); |
281 | 281 | ||
282 | reg = pack(info->var.yres_virtual, info->var.xres_virtual); | 282 | reg = pack(info->var.yres_virtual, info->var.xres_virtual); |
283 | outreg(disp, GC_L0WH_L0WW, reg); | 283 | outreg(disp, GC_L0WH_L0WW, reg); |
284 | return 0; | 284 | return 0; |
285 | } | 285 | } |
286 | 286 | ||
287 | static int mb862xxfb_blank(int mode, struct fb_info *fbi) | 287 | static int mb862xxfb_blank(int mode, struct fb_info *fbi) |
288 | { | 288 | { |
289 | struct mb862xxfb_par *par = fbi->par; | 289 | struct mb862xxfb_par *par = fbi->par; |
290 | unsigned long reg; | 290 | unsigned long reg; |
291 | 291 | ||
292 | dev_dbg(fbi->dev, "blank mode=%d\n", mode); | 292 | dev_dbg(fbi->dev, "blank mode=%d\n", mode); |
293 | 293 | ||
294 | switch (mode) { | 294 | switch (mode) { |
295 | case FB_BLANK_POWERDOWN: | 295 | case FB_BLANK_POWERDOWN: |
296 | reg = inreg(disp, GC_DCM1); | 296 | reg = inreg(disp, GC_DCM1); |
297 | reg &= ~GC_DCM01_DEN; | 297 | reg &= ~GC_DCM01_DEN; |
298 | outreg(disp, GC_DCM1, reg); | 298 | outreg(disp, GC_DCM1, reg); |
299 | break; | 299 | break; |
300 | case FB_BLANK_UNBLANK: | 300 | case FB_BLANK_UNBLANK: |
301 | reg = inreg(disp, GC_DCM1); | 301 | reg = inreg(disp, GC_DCM1); |
302 | reg |= GC_DCM01_DEN; | 302 | reg |= GC_DCM01_DEN; |
303 | outreg(disp, GC_DCM1, reg); | 303 | outreg(disp, GC_DCM1, reg); |
304 | break; | 304 | break; |
305 | case FB_BLANK_NORMAL: | 305 | case FB_BLANK_NORMAL: |
306 | case FB_BLANK_VSYNC_SUSPEND: | 306 | case FB_BLANK_VSYNC_SUSPEND: |
307 | case FB_BLANK_HSYNC_SUSPEND: | 307 | case FB_BLANK_HSYNC_SUSPEND: |
308 | default: | 308 | default: |
309 | return 1; | 309 | return 1; |
310 | } | 310 | } |
311 | return 0; | 311 | return 0; |
312 | } | 312 | } |
313 | 313 | ||
314 | static int mb862xxfb_ioctl(struct fb_info *fbi, unsigned int cmd, | 314 | static int mb862xxfb_ioctl(struct fb_info *fbi, unsigned int cmd, |
315 | unsigned long arg) | 315 | unsigned long arg) |
316 | { | 316 | { |
317 | struct mb862xxfb_par *par = fbi->par; | 317 | struct mb862xxfb_par *par = fbi->par; |
318 | struct mb862xx_l1_cfg *l1_cfg = &par->l1_cfg; | 318 | struct mb862xx_l1_cfg *l1_cfg = &par->l1_cfg; |
319 | void __user *argp = (void __user *)arg; | 319 | void __user *argp = (void __user *)arg; |
320 | int *enable; | 320 | int *enable; |
321 | u32 l1em = 0; | 321 | u32 l1em = 0; |
322 | 322 | ||
323 | switch (cmd) { | 323 | switch (cmd) { |
324 | case MB862XX_L1_GET_CFG: | 324 | case MB862XX_L1_GET_CFG: |
325 | if (copy_to_user(argp, l1_cfg, sizeof(*l1_cfg))) | 325 | if (copy_to_user(argp, l1_cfg, sizeof(*l1_cfg))) |
326 | return -EFAULT; | 326 | return -EFAULT; |
327 | break; | 327 | break; |
328 | case MB862XX_L1_SET_CFG: | 328 | case MB862XX_L1_SET_CFG: |
329 | if (copy_from_user(l1_cfg, argp, sizeof(*l1_cfg))) | 329 | if (copy_from_user(l1_cfg, argp, sizeof(*l1_cfg))) |
330 | return -EFAULT; | 330 | return -EFAULT; |
331 | if (l1_cfg->dh == 0 || l1_cfg->dw == 0) | ||
332 | return -EINVAL; | ||
331 | if ((l1_cfg->sw >= l1_cfg->dw) && (l1_cfg->sh >= l1_cfg->dh)) { | 333 | if ((l1_cfg->sw >= l1_cfg->dw) && (l1_cfg->sh >= l1_cfg->dh)) { |
332 | /* downscaling */ | 334 | /* downscaling */ |
333 | outreg(cap, GC_CAP_CSC, | 335 | outreg(cap, GC_CAP_CSC, |
334 | pack((l1_cfg->sh << 11) / l1_cfg->dh, | 336 | pack((l1_cfg->sh << 11) / l1_cfg->dh, |
335 | (l1_cfg->sw << 11) / l1_cfg->dw)); | 337 | (l1_cfg->sw << 11) / l1_cfg->dw)); |
336 | l1em = inreg(disp, GC_L1EM); | 338 | l1em = inreg(disp, GC_L1EM); |
337 | l1em &= ~GC_L1EM_DM; | 339 | l1em &= ~GC_L1EM_DM; |
338 | } else if ((l1_cfg->sw <= l1_cfg->dw) && | 340 | } else if ((l1_cfg->sw <= l1_cfg->dw) && |
339 | (l1_cfg->sh <= l1_cfg->dh)) { | 341 | (l1_cfg->sh <= l1_cfg->dh)) { |
340 | /* upscaling */ | 342 | /* upscaling */ |
341 | outreg(cap, GC_CAP_CSC, | 343 | outreg(cap, GC_CAP_CSC, |
342 | pack((l1_cfg->sh << 11) / l1_cfg->dh, | 344 | pack((l1_cfg->sh << 11) / l1_cfg->dh, |
343 | (l1_cfg->sw << 11) / l1_cfg->dw)); | 345 | (l1_cfg->sw << 11) / l1_cfg->dw)); |
344 | outreg(cap, GC_CAP_CMSS, | 346 | outreg(cap, GC_CAP_CMSS, |
345 | pack(l1_cfg->sw >> 1, l1_cfg->sh)); | 347 | pack(l1_cfg->sw >> 1, l1_cfg->sh)); |
346 | outreg(cap, GC_CAP_CMDS, | 348 | outreg(cap, GC_CAP_CMDS, |
347 | pack(l1_cfg->dw >> 1, l1_cfg->dh)); | 349 | pack(l1_cfg->dw >> 1, l1_cfg->dh)); |
348 | l1em = inreg(disp, GC_L1EM); | 350 | l1em = inreg(disp, GC_L1EM); |
349 | l1em |= GC_L1EM_DM; | 351 | l1em |= GC_L1EM_DM; |
350 | } | 352 | } |
351 | 353 | ||
352 | if (l1_cfg->mirror) { | 354 | if (l1_cfg->mirror) { |
353 | outreg(cap, GC_CAP_CBM, | 355 | outreg(cap, GC_CAP_CBM, |
354 | inreg(cap, GC_CAP_CBM) | GC_CBM_HRV); | 356 | inreg(cap, GC_CAP_CBM) | GC_CBM_HRV); |
355 | l1em |= l1_cfg->dw * 2 - 8; | 357 | l1em |= l1_cfg->dw * 2 - 8; |
356 | } else { | 358 | } else { |
357 | outreg(cap, GC_CAP_CBM, | 359 | outreg(cap, GC_CAP_CBM, |
358 | inreg(cap, GC_CAP_CBM) & ~GC_CBM_HRV); | 360 | inreg(cap, GC_CAP_CBM) & ~GC_CBM_HRV); |
359 | l1em &= 0xffff0000; | 361 | l1em &= 0xffff0000; |
360 | } | 362 | } |
361 | outreg(disp, GC_L1EM, l1em); | 363 | outreg(disp, GC_L1EM, l1em); |
362 | break; | 364 | break; |
363 | case MB862XX_L1_ENABLE: | 365 | case MB862XX_L1_ENABLE: |
364 | enable = (int *)arg; | 366 | enable = (int *)arg; |
365 | if (*enable) { | 367 | if (*enable) { |
366 | outreg(disp, GC_L1DA, par->cap_buf); | 368 | outreg(disp, GC_L1DA, par->cap_buf); |
367 | outreg(cap, GC_CAP_IMG_START, | 369 | outreg(cap, GC_CAP_IMG_START, |
368 | pack(l1_cfg->sy >> 1, l1_cfg->sx)); | 370 | pack(l1_cfg->sy >> 1, l1_cfg->sx)); |
369 | outreg(cap, GC_CAP_IMG_END, | 371 | outreg(cap, GC_CAP_IMG_END, |
370 | pack(l1_cfg->sh, l1_cfg->sw)); | 372 | pack(l1_cfg->sh, l1_cfg->sw)); |
371 | outreg(disp, GC_L1M, GC_L1M_16 | GC_L1M_YC | GC_L1M_CS | | 373 | outreg(disp, GC_L1M, GC_L1M_16 | GC_L1M_YC | GC_L1M_CS | |
372 | (par->l1_stride << 16)); | 374 | (par->l1_stride << 16)); |
373 | outreg(disp, GC_L1WY_L1WX, | 375 | outreg(disp, GC_L1WY_L1WX, |
374 | pack(l1_cfg->dy, l1_cfg->dx)); | 376 | pack(l1_cfg->dy, l1_cfg->dx)); |
375 | outreg(disp, GC_L1WH_L1WW, | 377 | outreg(disp, GC_L1WH_L1WW, |
376 | pack(l1_cfg->dh - 1, l1_cfg->dw)); | 378 | pack(l1_cfg->dh - 1, l1_cfg->dw)); |
377 | outreg(disp, GC_DLS, 1); | 379 | outreg(disp, GC_DLS, 1); |
378 | outreg(cap, GC_CAP_VCM, | 380 | outreg(cap, GC_CAP_VCM, |
379 | GC_VCM_VIE | GC_VCM_CM | GC_VCM_VS_PAL); | 381 | GC_VCM_VIE | GC_VCM_CM | GC_VCM_VS_PAL); |
380 | outreg(disp, GC_DCM1, inreg(disp, GC_DCM1) | | 382 | outreg(disp, GC_DCM1, inreg(disp, GC_DCM1) | |
381 | GC_DCM1_DEN | GC_DCM1_L1E); | 383 | GC_DCM1_DEN | GC_DCM1_L1E); |
382 | } else { | 384 | } else { |
383 | outreg(cap, GC_CAP_VCM, | 385 | outreg(cap, GC_CAP_VCM, |
384 | inreg(cap, GC_CAP_VCM) & ~GC_VCM_VIE); | 386 | inreg(cap, GC_CAP_VCM) & ~GC_VCM_VIE); |
385 | outreg(disp, GC_DCM1, | 387 | outreg(disp, GC_DCM1, |
386 | inreg(disp, GC_DCM1) & ~GC_DCM1_L1E); | 388 | inreg(disp, GC_DCM1) & ~GC_DCM1_L1E); |
387 | } | 389 | } |
388 | break; | 390 | break; |
389 | case MB862XX_L1_CAP_CTL: | 391 | case MB862XX_L1_CAP_CTL: |
390 | enable = (int *)arg; | 392 | enable = (int *)arg; |
391 | if (*enable) { | 393 | if (*enable) { |
392 | outreg(cap, GC_CAP_VCM, | 394 | outreg(cap, GC_CAP_VCM, |
393 | inreg(cap, GC_CAP_VCM) | GC_VCM_VIE); | 395 | inreg(cap, GC_CAP_VCM) | GC_VCM_VIE); |
394 | } else { | 396 | } else { |
395 | outreg(cap, GC_CAP_VCM, | 397 | outreg(cap, GC_CAP_VCM, |
396 | inreg(cap, GC_CAP_VCM) & ~GC_VCM_VIE); | 398 | inreg(cap, GC_CAP_VCM) & ~GC_VCM_VIE); |
397 | } | 399 | } |
398 | break; | 400 | break; |
399 | default: | 401 | default: |
400 | return -EINVAL; | 402 | return -EINVAL; |
401 | } | 403 | } |
402 | return 0; | 404 | return 0; |
403 | } | 405 | } |
404 | 406 | ||
405 | /* framebuffer ops */ | 407 | /* framebuffer ops */ |
406 | static struct fb_ops mb862xxfb_ops = { | 408 | static struct fb_ops mb862xxfb_ops = { |
407 | .owner = THIS_MODULE, | 409 | .owner = THIS_MODULE, |
408 | .fb_check_var = mb862xxfb_check_var, | 410 | .fb_check_var = mb862xxfb_check_var, |
409 | .fb_set_par = mb862xxfb_set_par, | 411 | .fb_set_par = mb862xxfb_set_par, |
410 | .fb_setcolreg = mb862xxfb_setcolreg, | 412 | .fb_setcolreg = mb862xxfb_setcolreg, |
411 | .fb_blank = mb862xxfb_blank, | 413 | .fb_blank = mb862xxfb_blank, |
412 | .fb_pan_display = mb862xxfb_pan, | 414 | .fb_pan_display = mb862xxfb_pan, |
413 | .fb_fillrect = cfb_fillrect, | 415 | .fb_fillrect = cfb_fillrect, |
414 | .fb_copyarea = cfb_copyarea, | 416 | .fb_copyarea = cfb_copyarea, |
415 | .fb_imageblit = cfb_imageblit, | 417 | .fb_imageblit = cfb_imageblit, |
416 | .fb_ioctl = mb862xxfb_ioctl, | 418 | .fb_ioctl = mb862xxfb_ioctl, |
417 | }; | 419 | }; |
418 | 420 | ||
419 | /* initialize fb_info data */ | 421 | /* initialize fb_info data */ |
420 | static int mb862xxfb_init_fbinfo(struct fb_info *fbi) | 422 | static int mb862xxfb_init_fbinfo(struct fb_info *fbi) |
421 | { | 423 | { |
422 | struct mb862xxfb_par *par = fbi->par; | 424 | struct mb862xxfb_par *par = fbi->par; |
423 | struct mb862xx_gc_mode *mode = par->gc_mode; | 425 | struct mb862xx_gc_mode *mode = par->gc_mode; |
424 | unsigned long reg; | 426 | unsigned long reg; |
425 | int stride; | 427 | int stride; |
426 | 428 | ||
427 | fbi->fbops = &mb862xxfb_ops; | 429 | fbi->fbops = &mb862xxfb_ops; |
428 | fbi->pseudo_palette = par->pseudo_palette; | 430 | fbi->pseudo_palette = par->pseudo_palette; |
429 | fbi->screen_base = par->fb_base; | 431 | fbi->screen_base = par->fb_base; |
430 | fbi->screen_size = par->mapped_vram; | 432 | fbi->screen_size = par->mapped_vram; |
431 | 433 | ||
432 | strcpy(fbi->fix.id, DRV_NAME); | 434 | strcpy(fbi->fix.id, DRV_NAME); |
433 | fbi->fix.smem_start = (unsigned long)par->fb_base_phys; | 435 | fbi->fix.smem_start = (unsigned long)par->fb_base_phys; |
434 | fbi->fix.mmio_start = (unsigned long)par->mmio_base_phys; | 436 | fbi->fix.mmio_start = (unsigned long)par->mmio_base_phys; |
435 | fbi->fix.mmio_len = par->mmio_len; | 437 | fbi->fix.mmio_len = par->mmio_len; |
436 | fbi->fix.accel = FB_ACCEL_NONE; | 438 | fbi->fix.accel = FB_ACCEL_NONE; |
437 | fbi->fix.type = FB_TYPE_PACKED_PIXELS; | 439 | fbi->fix.type = FB_TYPE_PACKED_PIXELS; |
438 | fbi->fix.type_aux = 0; | 440 | fbi->fix.type_aux = 0; |
439 | fbi->fix.xpanstep = 1; | 441 | fbi->fix.xpanstep = 1; |
440 | fbi->fix.ypanstep = 1; | 442 | fbi->fix.ypanstep = 1; |
441 | fbi->fix.ywrapstep = 0; | 443 | fbi->fix.ywrapstep = 0; |
442 | 444 | ||
443 | reg = inreg(disp, GC_DCM1); | 445 | reg = inreg(disp, GC_DCM1); |
444 | if (reg & GC_DCM01_DEN && reg & GC_DCM01_L0E) { | 446 | if (reg & GC_DCM01_DEN && reg & GC_DCM01_L0E) { |
445 | /* get the disp mode from active display cfg */ | 447 | /* get the disp mode from active display cfg */ |
446 | unsigned long sc = ((reg & GC_DCM01_SC) >> 8) + 1; | 448 | unsigned long sc = ((reg & GC_DCM01_SC) >> 8) + 1; |
447 | unsigned long hsp, vsp, ht, vt; | 449 | unsigned long hsp, vsp, ht, vt; |
448 | 450 | ||
449 | dev_dbg(par->dev, "using bootloader's disp. mode\n"); | 451 | dev_dbg(par->dev, "using bootloader's disp. mode\n"); |
450 | fbi->var.pixclock = (sc * 1000000) / par->refclk; | 452 | fbi->var.pixclock = (sc * 1000000) / par->refclk; |
451 | fbi->var.xres = (inreg(disp, GC_HDB_HDP) & 0x0fff) + 1; | 453 | fbi->var.xres = (inreg(disp, GC_HDB_HDP) & 0x0fff) + 1; |
452 | reg = inreg(disp, GC_VDP_VSP); | 454 | reg = inreg(disp, GC_VDP_VSP); |
453 | fbi->var.yres = ((reg >> 16) & 0x0fff) + 1; | 455 | fbi->var.yres = ((reg >> 16) & 0x0fff) + 1; |
454 | vsp = (reg & 0x0fff) + 1; | 456 | vsp = (reg & 0x0fff) + 1; |
455 | fbi->var.xres_virtual = fbi->var.xres; | 457 | fbi->var.xres_virtual = fbi->var.xres; |
456 | fbi->var.yres_virtual = fbi->var.yres; | 458 | fbi->var.yres_virtual = fbi->var.yres; |
457 | reg = inreg(disp, GC_L0EM); | 459 | reg = inreg(disp, GC_L0EM); |
458 | if (reg & GC_L0EM_L0EC_24) { | 460 | if (reg & GC_L0EM_L0EC_24) { |
459 | fbi->var.bits_per_pixel = 32; | 461 | fbi->var.bits_per_pixel = 32; |
460 | } else { | 462 | } else { |
461 | reg = inreg(disp, GC_L0M); | 463 | reg = inreg(disp, GC_L0M); |
462 | if (reg & GC_L0M_L0C_16) | 464 | if (reg & GC_L0M_L0C_16) |
463 | fbi->var.bits_per_pixel = 16; | 465 | fbi->var.bits_per_pixel = 16; |
464 | else | 466 | else |
465 | fbi->var.bits_per_pixel = 8; | 467 | fbi->var.bits_per_pixel = 8; |
466 | } | 468 | } |
467 | reg = inreg(disp, GC_VSW_HSW_HSP); | 469 | reg = inreg(disp, GC_VSW_HSW_HSP); |
468 | fbi->var.hsync_len = ((reg & 0xff0000) >> 16) + 1; | 470 | fbi->var.hsync_len = ((reg & 0xff0000) >> 16) + 1; |
469 | fbi->var.vsync_len = ((reg & 0x3f000000) >> 24) + 1; | 471 | fbi->var.vsync_len = ((reg & 0x3f000000) >> 24) + 1; |
470 | hsp = (reg & 0xffff) + 1; | 472 | hsp = (reg & 0xffff) + 1; |
471 | ht = ((inreg(disp, GC_HTP) & 0xfff0000) >> 16) + 1; | 473 | ht = ((inreg(disp, GC_HTP) & 0xfff0000) >> 16) + 1; |
472 | fbi->var.right_margin = hsp - fbi->var.xres; | 474 | fbi->var.right_margin = hsp - fbi->var.xres; |
473 | fbi->var.left_margin = ht - hsp - fbi->var.hsync_len; | 475 | fbi->var.left_margin = ht - hsp - fbi->var.hsync_len; |
474 | vt = ((inreg(disp, GC_VTR) & 0xfff0000) >> 16) + 1; | 476 | vt = ((inreg(disp, GC_VTR) & 0xfff0000) >> 16) + 1; |
475 | fbi->var.lower_margin = vsp - fbi->var.yres; | 477 | fbi->var.lower_margin = vsp - fbi->var.yres; |
476 | fbi->var.upper_margin = vt - vsp - fbi->var.vsync_len; | 478 | fbi->var.upper_margin = vt - vsp - fbi->var.vsync_len; |
477 | } else if (mode) { | 479 | } else if (mode) { |
478 | dev_dbg(par->dev, "using supplied mode\n"); | 480 | dev_dbg(par->dev, "using supplied mode\n"); |
479 | fb_videomode_to_var(&fbi->var, (struct fb_videomode *)mode); | 481 | fb_videomode_to_var(&fbi->var, (struct fb_videomode *)mode); |
480 | fbi->var.bits_per_pixel = mode->def_bpp ? mode->def_bpp : 8; | 482 | fbi->var.bits_per_pixel = mode->def_bpp ? mode->def_bpp : 8; |
481 | } else { | 483 | } else { |
482 | int ret; | 484 | int ret; |
483 | 485 | ||
484 | ret = fb_find_mode(&fbi->var, fbi, "640x480-16@60", | 486 | ret = fb_find_mode(&fbi->var, fbi, "640x480-16@60", |
485 | NULL, 0, NULL, 16); | 487 | NULL, 0, NULL, 16); |
486 | if (ret == 0 || ret == 4) { | 488 | if (ret == 0 || ret == 4) { |
487 | dev_err(par->dev, | 489 | dev_err(par->dev, |
488 | "failed to get initial mode\n"); | 490 | "failed to get initial mode\n"); |
489 | return -EINVAL; | 491 | return -EINVAL; |
490 | } | 492 | } |
491 | } | 493 | } |
492 | 494 | ||
493 | fbi->var.xoffset = 0; | 495 | fbi->var.xoffset = 0; |
494 | fbi->var.yoffset = 0; | 496 | fbi->var.yoffset = 0; |
495 | fbi->var.grayscale = 0; | 497 | fbi->var.grayscale = 0; |
496 | fbi->var.nonstd = 0; | 498 | fbi->var.nonstd = 0; |
497 | fbi->var.height = -1; | 499 | fbi->var.height = -1; |
498 | fbi->var.width = -1; | 500 | fbi->var.width = -1; |
499 | fbi->var.accel_flags = 0; | 501 | fbi->var.accel_flags = 0; |
500 | fbi->var.vmode = FB_VMODE_NONINTERLACED; | 502 | fbi->var.vmode = FB_VMODE_NONINTERLACED; |
501 | fbi->var.activate = FB_ACTIVATE_NOW; | 503 | fbi->var.activate = FB_ACTIVATE_NOW; |
502 | fbi->flags = FBINFO_DEFAULT | | 504 | fbi->flags = FBINFO_DEFAULT | |
503 | #ifdef __BIG_ENDIAN | 505 | #ifdef __BIG_ENDIAN |
504 | FBINFO_FOREIGN_ENDIAN | | 506 | FBINFO_FOREIGN_ENDIAN | |
505 | #endif | 507 | #endif |
506 | FBINFO_HWACCEL_XPAN | | 508 | FBINFO_HWACCEL_XPAN | |
507 | FBINFO_HWACCEL_YPAN; | 509 | FBINFO_HWACCEL_YPAN; |
508 | 510 | ||
509 | /* check and possibly fix bpp */ | 511 | /* check and possibly fix bpp */ |
510 | if ((fbi->fbops->fb_check_var)(&fbi->var, fbi)) | 512 | if ((fbi->fbops->fb_check_var)(&fbi->var, fbi)) |
511 | dev_err(par->dev, "check_var() failed on initial setup?\n"); | 513 | dev_err(par->dev, "check_var() failed on initial setup?\n"); |
512 | 514 | ||
513 | fbi->fix.visual = fbi->var.bits_per_pixel == 8 ? | 515 | fbi->fix.visual = fbi->var.bits_per_pixel == 8 ? |
514 | FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; | 516 | FB_VISUAL_PSEUDOCOLOR : FB_VISUAL_TRUECOLOR; |
515 | fbi->fix.line_length = (fbi->var.xres_virtual * | 517 | fbi->fix.line_length = (fbi->var.xres_virtual * |
516 | fbi->var.bits_per_pixel) / 8; | 518 | fbi->var.bits_per_pixel) / 8; |
517 | fbi->fix.smem_len = fbi->fix.line_length * fbi->var.yres_virtual; | 519 | fbi->fix.smem_len = fbi->fix.line_length * fbi->var.yres_virtual; |
518 | 520 | ||
519 | /* | 521 | /* |
520 | * reserve space for capture buffers and two cursors | 522 | * reserve space for capture buffers and two cursors |
521 | * at the end of vram: 720x576 * 2 * 2.2 + 64x64 * 16. | 523 | * at the end of vram: 720x576 * 2 * 2.2 + 64x64 * 16. |
522 | */ | 524 | */ |
523 | par->cap_buf = par->mapped_vram - 0x1bd800 - 0x10000; | 525 | par->cap_buf = par->mapped_vram - 0x1bd800 - 0x10000; |
524 | par->cap_len = 0x1bd800; | 526 | par->cap_len = 0x1bd800; |
525 | par->l1_cfg.sx = 0; | 527 | par->l1_cfg.sx = 0; |
526 | par->l1_cfg.sy = 0; | 528 | par->l1_cfg.sy = 0; |
527 | par->l1_cfg.sw = 720; | 529 | par->l1_cfg.sw = 720; |
528 | par->l1_cfg.sh = 576; | 530 | par->l1_cfg.sh = 576; |
529 | par->l1_cfg.dx = 0; | 531 | par->l1_cfg.dx = 0; |
530 | par->l1_cfg.dy = 0; | 532 | par->l1_cfg.dy = 0; |
531 | par->l1_cfg.dw = 720; | 533 | par->l1_cfg.dw = 720; |
532 | par->l1_cfg.dh = 576; | 534 | par->l1_cfg.dh = 576; |
533 | stride = par->l1_cfg.sw * (fbi->var.bits_per_pixel / 8); | 535 | stride = par->l1_cfg.sw * (fbi->var.bits_per_pixel / 8); |
534 | par->l1_stride = stride / 64 + ((stride % 64) ? 1 : 0); | 536 | par->l1_stride = stride / 64 + ((stride % 64) ? 1 : 0); |
535 | outreg(cap, GC_CAP_CBM, GC_CBM_OO | GC_CBM_CBST | | 537 | outreg(cap, GC_CAP_CBM, GC_CBM_OO | GC_CBM_CBST | |
536 | (par->l1_stride << 16)); | 538 | (par->l1_stride << 16)); |
537 | outreg(cap, GC_CAP_CBOA, par->cap_buf); | 539 | outreg(cap, GC_CAP_CBOA, par->cap_buf); |
538 | outreg(cap, GC_CAP_CBLA, par->cap_buf + par->cap_len); | 540 | outreg(cap, GC_CAP_CBLA, par->cap_buf + par->cap_len); |
539 | return 0; | 541 | return 0; |
540 | } | 542 | } |
541 | 543 | ||
542 | /* | 544 | /* |
543 | * show some display controller and cursor registers | 545 | * show some display controller and cursor registers |
544 | */ | 546 | */ |
545 | static ssize_t mb862xxfb_show_dispregs(struct device *dev, | 547 | static ssize_t mb862xxfb_show_dispregs(struct device *dev, |
546 | struct device_attribute *attr, char *buf) | 548 | struct device_attribute *attr, char *buf) |
547 | { | 549 | { |
548 | struct fb_info *fbi = dev_get_drvdata(dev); | 550 | struct fb_info *fbi = dev_get_drvdata(dev); |
549 | struct mb862xxfb_par *par = fbi->par; | 551 | struct mb862xxfb_par *par = fbi->par; |
550 | char *ptr = buf; | 552 | char *ptr = buf; |
551 | unsigned int reg; | 553 | unsigned int reg; |
552 | 554 | ||
553 | for (reg = GC_DCM0; reg <= GC_L0DY_L0DX; reg += 4) | 555 | for (reg = GC_DCM0; reg <= GC_L0DY_L0DX; reg += 4) |
554 | ptr += sprintf(ptr, "%08x = %08x\n", | 556 | ptr += sprintf(ptr, "%08x = %08x\n", |
555 | reg, inreg(disp, reg)); | 557 | reg, inreg(disp, reg)); |
556 | 558 | ||
557 | for (reg = GC_CPM_CUTC; reg <= GC_CUY1_CUX1; reg += 4) | 559 | for (reg = GC_CPM_CUTC; reg <= GC_CUY1_CUX1; reg += 4) |
558 | ptr += sprintf(ptr, "%08x = %08x\n", | 560 | ptr += sprintf(ptr, "%08x = %08x\n", |
559 | reg, inreg(disp, reg)); | 561 | reg, inreg(disp, reg)); |
560 | 562 | ||
561 | for (reg = GC_DCM1; reg <= GC_L0WH_L0WW; reg += 4) | 563 | for (reg = GC_DCM1; reg <= GC_L0WH_L0WW; reg += 4) |
562 | ptr += sprintf(ptr, "%08x = %08x\n", | 564 | ptr += sprintf(ptr, "%08x = %08x\n", |
563 | reg, inreg(disp, reg)); | 565 | reg, inreg(disp, reg)); |
564 | 566 | ||
565 | for (reg = 0x400; reg <= 0x410; reg += 4) | 567 | for (reg = 0x400; reg <= 0x410; reg += 4) |
566 | ptr += sprintf(ptr, "geo %08x = %08x\n", | 568 | ptr += sprintf(ptr, "geo %08x = %08x\n", |
567 | reg, inreg(geo, reg)); | 569 | reg, inreg(geo, reg)); |
568 | 570 | ||
569 | for (reg = 0x400; reg <= 0x410; reg += 4) | 571 | for (reg = 0x400; reg <= 0x410; reg += 4) |
570 | ptr += sprintf(ptr, "draw %08x = %08x\n", | 572 | ptr += sprintf(ptr, "draw %08x = %08x\n", |
571 | reg, inreg(draw, reg)); | 573 | reg, inreg(draw, reg)); |
572 | 574 | ||
573 | for (reg = 0x440; reg <= 0x450; reg += 4) | 575 | for (reg = 0x440; reg <= 0x450; reg += 4) |
574 | ptr += sprintf(ptr, "draw %08x = %08x\n", | 576 | ptr += sprintf(ptr, "draw %08x = %08x\n", |
575 | reg, inreg(draw, reg)); | 577 | reg, inreg(draw, reg)); |
576 | 578 | ||
577 | return ptr - buf; | 579 | return ptr - buf; |
578 | } | 580 | } |
579 | 581 | ||
580 | static DEVICE_ATTR(dispregs, 0444, mb862xxfb_show_dispregs, NULL); | 582 | static DEVICE_ATTR(dispregs, 0444, mb862xxfb_show_dispregs, NULL); |
581 | 583 | ||
582 | static irqreturn_t mb862xx_intr(int irq, void *dev_id) | 584 | static irqreturn_t mb862xx_intr(int irq, void *dev_id) |
583 | { | 585 | { |
584 | struct mb862xxfb_par *par = (struct mb862xxfb_par *) dev_id; | 586 | struct mb862xxfb_par *par = (struct mb862xxfb_par *) dev_id; |
585 | unsigned long reg_ist, mask; | 587 | unsigned long reg_ist, mask; |
586 | 588 | ||
587 | if (!par) | 589 | if (!par) |
588 | return IRQ_NONE; | 590 | return IRQ_NONE; |
589 | 591 | ||
590 | if (par->type == BT_CARMINE) { | 592 | if (par->type == BT_CARMINE) { |
591 | /* Get Interrupt Status */ | 593 | /* Get Interrupt Status */ |
592 | reg_ist = inreg(ctrl, GC_CTRL_STATUS); | 594 | reg_ist = inreg(ctrl, GC_CTRL_STATUS); |
593 | mask = inreg(ctrl, GC_CTRL_INT_MASK); | 595 | mask = inreg(ctrl, GC_CTRL_INT_MASK); |
594 | if (reg_ist == 0) | 596 | if (reg_ist == 0) |
595 | return IRQ_HANDLED; | 597 | return IRQ_HANDLED; |
596 | 598 | ||
597 | reg_ist &= mask; | 599 | reg_ist &= mask; |
598 | if (reg_ist == 0) | 600 | if (reg_ist == 0) |
599 | return IRQ_HANDLED; | 601 | return IRQ_HANDLED; |
600 | 602 | ||
601 | /* Clear interrupt status */ | 603 | /* Clear interrupt status */ |
602 | outreg(ctrl, 0x0, reg_ist); | 604 | outreg(ctrl, 0x0, reg_ist); |
603 | } else { | 605 | } else { |
604 | /* Get status */ | 606 | /* Get status */ |
605 | reg_ist = inreg(host, GC_IST); | 607 | reg_ist = inreg(host, GC_IST); |
606 | mask = inreg(host, GC_IMASK); | 608 | mask = inreg(host, GC_IMASK); |
607 | 609 | ||
608 | reg_ist &= mask; | 610 | reg_ist &= mask; |
609 | if (reg_ist == 0) | 611 | if (reg_ist == 0) |
610 | return IRQ_HANDLED; | 612 | return IRQ_HANDLED; |
611 | 613 | ||
612 | /* Clear status */ | 614 | /* Clear status */ |
613 | outreg(host, GC_IST, ~reg_ist); | 615 | outreg(host, GC_IST, ~reg_ist); |
614 | } | 616 | } |
615 | return IRQ_HANDLED; | 617 | return IRQ_HANDLED; |
616 | } | 618 | } |
617 | 619 | ||
618 | #if defined(CONFIG_FB_MB862XX_LIME) | 620 | #if defined(CONFIG_FB_MB862XX_LIME) |
619 | /* | 621 | /* |
620 | * GDC (Lime, Coral(B/Q), Mint, ...) on host bus | 622 | * GDC (Lime, Coral(B/Q), Mint, ...) on host bus |
621 | */ | 623 | */ |
622 | static int mb862xx_gdc_init(struct mb862xxfb_par *par) | 624 | static int mb862xx_gdc_init(struct mb862xxfb_par *par) |
623 | { | 625 | { |
624 | unsigned long ccf, mmr; | 626 | unsigned long ccf, mmr; |
625 | unsigned long ver, rev; | 627 | unsigned long ver, rev; |
626 | 628 | ||
627 | if (!par) | 629 | if (!par) |
628 | return -ENODEV; | 630 | return -ENODEV; |
629 | 631 | ||
630 | #if defined(CONFIG_FB_PRE_INIT_FB) | 632 | #if defined(CONFIG_FB_PRE_INIT_FB) |
631 | par->pre_init = 1; | 633 | par->pre_init = 1; |
632 | #endif | 634 | #endif |
633 | par->host = par->mmio_base; | 635 | par->host = par->mmio_base; |
634 | par->i2c = par->mmio_base + MB862XX_I2C_BASE; | 636 | par->i2c = par->mmio_base + MB862XX_I2C_BASE; |
635 | par->disp = par->mmio_base + MB862XX_DISP_BASE; | 637 | par->disp = par->mmio_base + MB862XX_DISP_BASE; |
636 | par->cap = par->mmio_base + MB862XX_CAP_BASE; | 638 | par->cap = par->mmio_base + MB862XX_CAP_BASE; |
637 | par->draw = par->mmio_base + MB862XX_DRAW_BASE; | 639 | par->draw = par->mmio_base + MB862XX_DRAW_BASE; |
638 | par->geo = par->mmio_base + MB862XX_GEO_BASE; | 640 | par->geo = par->mmio_base + MB862XX_GEO_BASE; |
639 | par->pio = par->mmio_base + MB862XX_PIO_BASE; | 641 | par->pio = par->mmio_base + MB862XX_PIO_BASE; |
640 | 642 | ||
641 | par->refclk = GC_DISP_REFCLK_400; | 643 | par->refclk = GC_DISP_REFCLK_400; |
642 | 644 | ||
643 | ver = inreg(host, GC_CID); | 645 | ver = inreg(host, GC_CID); |
644 | rev = inreg(pio, GC_REVISION); | 646 | rev = inreg(pio, GC_REVISION); |
645 | if ((ver == 0x303) && (rev & 0xffffff00) == 0x20050100) { | 647 | if ((ver == 0x303) && (rev & 0xffffff00) == 0x20050100) { |
646 | dev_info(par->dev, "Fujitsu Lime v1.%d found\n", | 648 | dev_info(par->dev, "Fujitsu Lime v1.%d found\n", |
647 | (int)rev & 0xff); | 649 | (int)rev & 0xff); |
648 | par->type = BT_LIME; | 650 | par->type = BT_LIME; |
649 | ccf = par->gc_mode ? par->gc_mode->ccf : GC_CCF_COT_100; | 651 | ccf = par->gc_mode ? par->gc_mode->ccf : GC_CCF_COT_100; |
650 | mmr = par->gc_mode ? par->gc_mode->mmr : 0x414fb7f2; | 652 | mmr = par->gc_mode ? par->gc_mode->mmr : 0x414fb7f2; |
651 | } else { | 653 | } else { |
652 | dev_info(par->dev, "? GDC, CID/Rev.: 0x%lx/0x%lx \n", ver, rev); | 654 | dev_info(par->dev, "? GDC, CID/Rev.: 0x%lx/0x%lx \n", ver, rev); |
653 | return -ENODEV; | 655 | return -ENODEV; |
654 | } | 656 | } |
655 | 657 | ||
656 | if (!par->pre_init) { | 658 | if (!par->pre_init) { |
657 | outreg(host, GC_CCF, ccf); | 659 | outreg(host, GC_CCF, ccf); |
658 | udelay(200); | 660 | udelay(200); |
659 | outreg(host, GC_MMR, mmr); | 661 | outreg(host, GC_MMR, mmr); |
660 | udelay(10); | 662 | udelay(10); |
661 | } | 663 | } |
662 | 664 | ||
663 | /* interrupt status */ | 665 | /* interrupt status */ |
664 | outreg(host, GC_IST, 0); | 666 | outreg(host, GC_IST, 0); |
665 | outreg(host, GC_IMASK, GC_INT_EN); | 667 | outreg(host, GC_IMASK, GC_INT_EN); |
666 | return 0; | 668 | return 0; |
667 | } | 669 | } |
668 | 670 | ||
669 | static int __devinit of_platform_mb862xx_probe(struct platform_device *ofdev) | 671 | static int __devinit of_platform_mb862xx_probe(struct platform_device *ofdev) |
670 | { | 672 | { |
671 | struct device_node *np = ofdev->dev.of_node; | 673 | struct device_node *np = ofdev->dev.of_node; |
672 | struct device *dev = &ofdev->dev; | 674 | struct device *dev = &ofdev->dev; |
673 | struct mb862xxfb_par *par; | 675 | struct mb862xxfb_par *par; |
674 | struct fb_info *info; | 676 | struct fb_info *info; |
675 | struct resource res; | 677 | struct resource res; |
676 | resource_size_t res_size; | 678 | resource_size_t res_size; |
677 | unsigned long ret = -ENODEV; | 679 | unsigned long ret = -ENODEV; |
678 | 680 | ||
679 | if (of_address_to_resource(np, 0, &res)) { | 681 | if (of_address_to_resource(np, 0, &res)) { |
680 | dev_err(dev, "Invalid address\n"); | 682 | dev_err(dev, "Invalid address\n"); |
681 | return -ENXIO; | 683 | return -ENXIO; |
682 | } | 684 | } |
683 | 685 | ||
684 | info = framebuffer_alloc(sizeof(struct mb862xxfb_par), dev); | 686 | info = framebuffer_alloc(sizeof(struct mb862xxfb_par), dev); |
685 | if (info == NULL) { | 687 | if (info == NULL) { |
686 | dev_err(dev, "cannot allocate framebuffer\n"); | 688 | dev_err(dev, "cannot allocate framebuffer\n"); |
687 | return -ENOMEM; | 689 | return -ENOMEM; |
688 | } | 690 | } |
689 | 691 | ||
690 | par = info->par; | 692 | par = info->par; |
691 | par->info = info; | 693 | par->info = info; |
692 | par->dev = dev; | 694 | par->dev = dev; |
693 | 695 | ||
694 | par->irq = irq_of_parse_and_map(np, 0); | 696 | par->irq = irq_of_parse_and_map(np, 0); |
695 | if (par->irq == NO_IRQ) { | 697 | if (par->irq == NO_IRQ) { |
696 | dev_err(dev, "failed to map irq\n"); | 698 | dev_err(dev, "failed to map irq\n"); |
697 | ret = -ENODEV; | 699 | ret = -ENODEV; |
698 | goto fbrel; | 700 | goto fbrel; |
699 | } | 701 | } |
700 | 702 | ||
701 | res_size = resource_size(&res); | 703 | res_size = resource_size(&res); |
702 | par->res = request_mem_region(res.start, res_size, DRV_NAME); | 704 | par->res = request_mem_region(res.start, res_size, DRV_NAME); |
703 | if (par->res == NULL) { | 705 | if (par->res == NULL) { |
704 | dev_err(dev, "Cannot claim framebuffer/mmio\n"); | 706 | dev_err(dev, "Cannot claim framebuffer/mmio\n"); |
705 | ret = -ENXIO; | 707 | ret = -ENXIO; |
706 | goto irqdisp; | 708 | goto irqdisp; |
707 | } | 709 | } |
708 | 710 | ||
709 | #if defined(CONFIG_SOCRATES) | 711 | #if defined(CONFIG_SOCRATES) |
710 | par->gc_mode = &socrates_gc_mode; | 712 | par->gc_mode = &socrates_gc_mode; |
711 | #endif | 713 | #endif |
712 | 714 | ||
713 | par->fb_base_phys = res.start; | 715 | par->fb_base_phys = res.start; |
714 | par->mmio_base_phys = res.start + MB862XX_MMIO_BASE; | 716 | par->mmio_base_phys = res.start + MB862XX_MMIO_BASE; |
715 | par->mmio_len = MB862XX_MMIO_SIZE; | 717 | par->mmio_len = MB862XX_MMIO_SIZE; |
716 | if (par->gc_mode) | 718 | if (par->gc_mode) |
717 | par->mapped_vram = par->gc_mode->max_vram; | 719 | par->mapped_vram = par->gc_mode->max_vram; |
718 | else | 720 | else |
719 | par->mapped_vram = MB862XX_MEM_SIZE; | 721 | par->mapped_vram = MB862XX_MEM_SIZE; |
720 | 722 | ||
721 | par->fb_base = ioremap(par->fb_base_phys, par->mapped_vram); | 723 | par->fb_base = ioremap(par->fb_base_phys, par->mapped_vram); |
722 | if (par->fb_base == NULL) { | 724 | if (par->fb_base == NULL) { |
723 | dev_err(dev, "Cannot map framebuffer\n"); | 725 | dev_err(dev, "Cannot map framebuffer\n"); |
724 | goto rel_reg; | 726 | goto rel_reg; |
725 | } | 727 | } |
726 | 728 | ||
727 | par->mmio_base = ioremap(par->mmio_base_phys, par->mmio_len); | 729 | par->mmio_base = ioremap(par->mmio_base_phys, par->mmio_len); |
728 | if (par->mmio_base == NULL) { | 730 | if (par->mmio_base == NULL) { |
729 | dev_err(dev, "Cannot map registers\n"); | 731 | dev_err(dev, "Cannot map registers\n"); |
730 | goto fb_unmap; | 732 | goto fb_unmap; |
731 | } | 733 | } |
732 | 734 | ||
733 | dev_dbg(dev, "fb phys 0x%llx 0x%lx\n", | 735 | dev_dbg(dev, "fb phys 0x%llx 0x%lx\n", |
734 | (u64)par->fb_base_phys, (ulong)par->mapped_vram); | 736 | (u64)par->fb_base_phys, (ulong)par->mapped_vram); |
735 | dev_dbg(dev, "mmio phys 0x%llx 0x%lx, (irq = %d)\n", | 737 | dev_dbg(dev, "mmio phys 0x%llx 0x%lx, (irq = %d)\n", |
736 | (u64)par->mmio_base_phys, (ulong)par->mmio_len, par->irq); | 738 | (u64)par->mmio_base_phys, (ulong)par->mmio_len, par->irq); |
737 | 739 | ||
738 | if (mb862xx_gdc_init(par)) | 740 | if (mb862xx_gdc_init(par)) |
739 | goto io_unmap; | 741 | goto io_unmap; |
740 | 742 | ||
741 | if (request_irq(par->irq, mb862xx_intr, 0, | 743 | if (request_irq(par->irq, mb862xx_intr, 0, |
742 | DRV_NAME, (void *)par)) { | 744 | DRV_NAME, (void *)par)) { |
743 | dev_err(dev, "Cannot request irq\n"); | 745 | dev_err(dev, "Cannot request irq\n"); |
744 | goto io_unmap; | 746 | goto io_unmap; |
745 | } | 747 | } |
746 | 748 | ||
747 | mb862xxfb_init_fbinfo(info); | 749 | mb862xxfb_init_fbinfo(info); |
748 | 750 | ||
749 | if (fb_alloc_cmap(&info->cmap, NR_PALETTE, 0) < 0) { | 751 | if (fb_alloc_cmap(&info->cmap, NR_PALETTE, 0) < 0) { |
750 | dev_err(dev, "Could not allocate cmap for fb_info.\n"); | 752 | dev_err(dev, "Could not allocate cmap for fb_info.\n"); |
751 | goto free_irq; | 753 | goto free_irq; |
752 | } | 754 | } |
753 | 755 | ||
754 | if ((info->fbops->fb_set_par)(info)) | 756 | if ((info->fbops->fb_set_par)(info)) |
755 | dev_err(dev, "set_var() failed on initial setup?\n"); | 757 | dev_err(dev, "set_var() failed on initial setup?\n"); |
756 | 758 | ||
757 | if (register_framebuffer(info)) { | 759 | if (register_framebuffer(info)) { |
758 | dev_err(dev, "failed to register framebuffer\n"); | 760 | dev_err(dev, "failed to register framebuffer\n"); |
759 | goto rel_cmap; | 761 | goto rel_cmap; |
760 | } | 762 | } |
761 | 763 | ||
762 | dev_set_drvdata(dev, info); | 764 | dev_set_drvdata(dev, info); |
763 | 765 | ||
764 | if (device_create_file(dev, &dev_attr_dispregs)) | 766 | if (device_create_file(dev, &dev_attr_dispregs)) |
765 | dev_err(dev, "Can't create sysfs regdump file\n"); | 767 | dev_err(dev, "Can't create sysfs regdump file\n"); |
766 | return 0; | 768 | return 0; |
767 | 769 | ||
768 | rel_cmap: | 770 | rel_cmap: |
769 | fb_dealloc_cmap(&info->cmap); | 771 | fb_dealloc_cmap(&info->cmap); |
770 | free_irq: | 772 | free_irq: |
771 | outreg(host, GC_IMASK, 0); | 773 | outreg(host, GC_IMASK, 0); |
772 | free_irq(par->irq, (void *)par); | 774 | free_irq(par->irq, (void *)par); |
773 | io_unmap: | 775 | io_unmap: |
774 | iounmap(par->mmio_base); | 776 | iounmap(par->mmio_base); |
775 | fb_unmap: | 777 | fb_unmap: |
776 | iounmap(par->fb_base); | 778 | iounmap(par->fb_base); |
777 | rel_reg: | 779 | rel_reg: |
778 | release_mem_region(res.start, res_size); | 780 | release_mem_region(res.start, res_size); |
779 | irqdisp: | 781 | irqdisp: |
780 | irq_dispose_mapping(par->irq); | 782 | irq_dispose_mapping(par->irq); |
781 | fbrel: | 783 | fbrel: |
782 | dev_set_drvdata(dev, NULL); | 784 | dev_set_drvdata(dev, NULL); |
783 | framebuffer_release(info); | 785 | framebuffer_release(info); |
784 | return ret; | 786 | return ret; |
785 | } | 787 | } |
786 | 788 | ||
787 | static int __devexit of_platform_mb862xx_remove(struct platform_device *ofdev) | 789 | static int __devexit of_platform_mb862xx_remove(struct platform_device *ofdev) |
788 | { | 790 | { |
789 | struct fb_info *fbi = dev_get_drvdata(&ofdev->dev); | 791 | struct fb_info *fbi = dev_get_drvdata(&ofdev->dev); |
790 | struct mb862xxfb_par *par = fbi->par; | 792 | struct mb862xxfb_par *par = fbi->par; |
791 | resource_size_t res_size = resource_size(par->res); | 793 | resource_size_t res_size = resource_size(par->res); |
792 | unsigned long reg; | 794 | unsigned long reg; |
793 | 795 | ||
794 | dev_dbg(fbi->dev, "%s release\n", fbi->fix.id); | 796 | dev_dbg(fbi->dev, "%s release\n", fbi->fix.id); |
795 | 797 | ||
796 | /* display off */ | 798 | /* display off */ |
797 | reg = inreg(disp, GC_DCM1); | 799 | reg = inreg(disp, GC_DCM1); |
798 | reg &= ~(GC_DCM01_DEN | GC_DCM01_L0E); | 800 | reg &= ~(GC_DCM01_DEN | GC_DCM01_L0E); |
799 | outreg(disp, GC_DCM1, reg); | 801 | outreg(disp, GC_DCM1, reg); |
800 | 802 | ||
801 | /* disable interrupts */ | 803 | /* disable interrupts */ |
802 | outreg(host, GC_IMASK, 0); | 804 | outreg(host, GC_IMASK, 0); |
803 | 805 | ||
804 | free_irq(par->irq, (void *)par); | 806 | free_irq(par->irq, (void *)par); |
805 | irq_dispose_mapping(par->irq); | 807 | irq_dispose_mapping(par->irq); |
806 | 808 | ||
807 | device_remove_file(&ofdev->dev, &dev_attr_dispregs); | 809 | device_remove_file(&ofdev->dev, &dev_attr_dispregs); |
808 | 810 | ||
809 | unregister_framebuffer(fbi); | 811 | unregister_framebuffer(fbi); |
810 | fb_dealloc_cmap(&fbi->cmap); | 812 | fb_dealloc_cmap(&fbi->cmap); |
811 | 813 | ||
812 | iounmap(par->mmio_base); | 814 | iounmap(par->mmio_base); |
813 | iounmap(par->fb_base); | 815 | iounmap(par->fb_base); |
814 | 816 | ||
815 | dev_set_drvdata(&ofdev->dev, NULL); | 817 | dev_set_drvdata(&ofdev->dev, NULL); |
816 | release_mem_region(par->res->start, res_size); | 818 | release_mem_region(par->res->start, res_size); |
817 | framebuffer_release(fbi); | 819 | framebuffer_release(fbi); |
818 | return 0; | 820 | return 0; |
819 | } | 821 | } |
820 | 822 | ||
821 | /* | 823 | /* |
822 | * common types | 824 | * common types |
823 | */ | 825 | */ |
824 | static struct of_device_id __devinitdata of_platform_mb862xx_tbl[] = { | 826 | static struct of_device_id __devinitdata of_platform_mb862xx_tbl[] = { |
825 | { .compatible = "fujitsu,MB86276", }, | 827 | { .compatible = "fujitsu,MB86276", }, |
826 | { .compatible = "fujitsu,lime", }, | 828 | { .compatible = "fujitsu,lime", }, |
827 | { .compatible = "fujitsu,MB86277", }, | 829 | { .compatible = "fujitsu,MB86277", }, |
828 | { .compatible = "fujitsu,mint", }, | 830 | { .compatible = "fujitsu,mint", }, |
829 | { .compatible = "fujitsu,MB86293", }, | 831 | { .compatible = "fujitsu,MB86293", }, |
830 | { .compatible = "fujitsu,MB86294", }, | 832 | { .compatible = "fujitsu,MB86294", }, |
831 | { .compatible = "fujitsu,coral", }, | 833 | { .compatible = "fujitsu,coral", }, |
832 | { /* end */ } | 834 | { /* end */ } |
833 | }; | 835 | }; |
834 | 836 | ||
835 | static struct platform_driver of_platform_mb862xxfb_driver = { | 837 | static struct platform_driver of_platform_mb862xxfb_driver = { |
836 | .driver = { | 838 | .driver = { |
837 | .name = DRV_NAME, | 839 | .name = DRV_NAME, |
838 | .owner = THIS_MODULE, | 840 | .owner = THIS_MODULE, |
839 | .of_match_table = of_platform_mb862xx_tbl, | 841 | .of_match_table = of_platform_mb862xx_tbl, |
840 | }, | 842 | }, |
841 | .probe = of_platform_mb862xx_probe, | 843 | .probe = of_platform_mb862xx_probe, |
842 | .remove = __devexit_p(of_platform_mb862xx_remove), | 844 | .remove = __devexit_p(of_platform_mb862xx_remove), |
843 | }; | 845 | }; |
844 | #endif | 846 | #endif |
845 | 847 | ||
846 | #if defined(CONFIG_FB_MB862XX_PCI_GDC) | 848 | #if defined(CONFIG_FB_MB862XX_PCI_GDC) |
847 | static int coralp_init(struct mb862xxfb_par *par) | 849 | static int coralp_init(struct mb862xxfb_par *par) |
848 | { | 850 | { |
849 | int cn, ver; | 851 | int cn, ver; |
850 | 852 | ||
851 | par->host = par->mmio_base; | 853 | par->host = par->mmio_base; |
852 | par->i2c = par->mmio_base + MB862XX_I2C_BASE; | 854 | par->i2c = par->mmio_base + MB862XX_I2C_BASE; |
853 | par->disp = par->mmio_base + MB862XX_DISP_BASE; | 855 | par->disp = par->mmio_base + MB862XX_DISP_BASE; |
854 | par->cap = par->mmio_base + MB862XX_CAP_BASE; | 856 | par->cap = par->mmio_base + MB862XX_CAP_BASE; |
855 | par->draw = par->mmio_base + MB862XX_DRAW_BASE; | 857 | par->draw = par->mmio_base + MB862XX_DRAW_BASE; |
856 | par->geo = par->mmio_base + MB862XX_GEO_BASE; | 858 | par->geo = par->mmio_base + MB862XX_GEO_BASE; |
857 | par->pio = par->mmio_base + MB862XX_PIO_BASE; | 859 | par->pio = par->mmio_base + MB862XX_PIO_BASE; |
858 | 860 | ||
859 | par->refclk = GC_DISP_REFCLK_400; | 861 | par->refclk = GC_DISP_REFCLK_400; |
860 | 862 | ||
861 | if (par->mapped_vram >= 0x2000000) { | 863 | if (par->mapped_vram >= 0x2000000) { |
862 | /* relocate gdc registers space */ | 864 | /* relocate gdc registers space */ |
863 | writel(1, par->fb_base + MB862XX_MMIO_BASE + GC_RSW); | 865 | writel(1, par->fb_base + MB862XX_MMIO_BASE + GC_RSW); |
864 | udelay(1); /* wait at least 20 bus cycles */ | 866 | udelay(1); /* wait at least 20 bus cycles */ |
865 | } | 867 | } |
866 | 868 | ||
867 | ver = inreg(host, GC_CID); | 869 | ver = inreg(host, GC_CID); |
868 | cn = (ver & GC_CID_CNAME_MSK) >> 8; | 870 | cn = (ver & GC_CID_CNAME_MSK) >> 8; |
869 | ver = ver & GC_CID_VERSION_MSK; | 871 | ver = ver & GC_CID_VERSION_MSK; |
870 | if (cn == 3) { | 872 | if (cn == 3) { |
871 | unsigned long reg; | 873 | unsigned long reg; |
872 | 874 | ||
873 | dev_info(par->dev, "Fujitsu Coral-%s GDC Rev.%d found\n",\ | 875 | dev_info(par->dev, "Fujitsu Coral-%s GDC Rev.%d found\n",\ |
874 | (ver == 6) ? "P" : (ver == 8) ? "PA" : "?", | 876 | (ver == 6) ? "P" : (ver == 8) ? "PA" : "?", |
875 | par->pdev->revision); | 877 | par->pdev->revision); |
876 | reg = inreg(disp, GC_DCM1); | 878 | reg = inreg(disp, GC_DCM1); |
877 | if (reg & GC_DCM01_DEN && reg & GC_DCM01_L0E) | 879 | if (reg & GC_DCM01_DEN && reg & GC_DCM01_L0E) |
878 | par->pre_init = 1; | 880 | par->pre_init = 1; |
879 | 881 | ||
880 | if (!par->pre_init) { | 882 | if (!par->pre_init) { |
881 | outreg(host, GC_CCF, GC_CCF_CGE_166 | GC_CCF_COT_133); | 883 | outreg(host, GC_CCF, GC_CCF_CGE_166 | GC_CCF_COT_133); |
882 | udelay(200); | 884 | udelay(200); |
883 | outreg(host, GC_MMR, GC_MMR_CORALP_EVB_VAL); | 885 | outreg(host, GC_MMR, GC_MMR_CORALP_EVB_VAL); |
884 | udelay(10); | 886 | udelay(10); |
885 | } | 887 | } |
886 | /* Clear interrupt status */ | 888 | /* Clear interrupt status */ |
887 | outreg(host, GC_IST, 0); | 889 | outreg(host, GC_IST, 0); |
888 | } else { | 890 | } else { |
889 | return -ENODEV; | 891 | return -ENODEV; |
890 | } | 892 | } |
891 | 893 | ||
892 | mb862xx_i2c_init(par); | 894 | mb862xx_i2c_init(par); |
893 | return 0; | 895 | return 0; |
894 | } | 896 | } |
895 | 897 | ||
896 | static int init_dram_ctrl(struct mb862xxfb_par *par) | 898 | static int init_dram_ctrl(struct mb862xxfb_par *par) |
897 | { | 899 | { |
898 | unsigned long i = 0; | 900 | unsigned long i = 0; |
899 | 901 | ||
900 | /* | 902 | /* |
901 | * Set io mode first! Spec. says IC may be destroyed | 903 | * Set io mode first! Spec. says IC may be destroyed |
902 | * if not set to SSTL2/LVCMOS before init. | 904 | * if not set to SSTL2/LVCMOS before init. |
903 | */ | 905 | */ |
904 | outreg(dram_ctrl, GC_DCTL_IOCONT1_IOCONT0, GC_EVB_DCTL_IOCONT1_IOCONT0); | 906 | outreg(dram_ctrl, GC_DCTL_IOCONT1_IOCONT0, GC_EVB_DCTL_IOCONT1_IOCONT0); |
905 | 907 | ||
906 | /* DRAM init */ | 908 | /* DRAM init */ |
907 | outreg(dram_ctrl, GC_DCTL_MODE_ADD, GC_EVB_DCTL_MODE_ADD); | 909 | outreg(dram_ctrl, GC_DCTL_MODE_ADD, GC_EVB_DCTL_MODE_ADD); |
908 | outreg(dram_ctrl, GC_DCTL_SETTIME1_EMODE, GC_EVB_DCTL_SETTIME1_EMODE); | 910 | outreg(dram_ctrl, GC_DCTL_SETTIME1_EMODE, GC_EVB_DCTL_SETTIME1_EMODE); |
909 | outreg(dram_ctrl, GC_DCTL_REFRESH_SETTIME2, | 911 | outreg(dram_ctrl, GC_DCTL_REFRESH_SETTIME2, |
910 | GC_EVB_DCTL_REFRESH_SETTIME2); | 912 | GC_EVB_DCTL_REFRESH_SETTIME2); |
911 | outreg(dram_ctrl, GC_DCTL_RSV2_RSV1, GC_EVB_DCTL_RSV2_RSV1); | 913 | outreg(dram_ctrl, GC_DCTL_RSV2_RSV1, GC_EVB_DCTL_RSV2_RSV1); |
912 | outreg(dram_ctrl, GC_DCTL_DDRIF2_DDRIF1, GC_EVB_DCTL_DDRIF2_DDRIF1); | 914 | outreg(dram_ctrl, GC_DCTL_DDRIF2_DDRIF1, GC_EVB_DCTL_DDRIF2_DDRIF1); |
913 | outreg(dram_ctrl, GC_DCTL_RSV0_STATES, GC_EVB_DCTL_RSV0_STATES); | 915 | outreg(dram_ctrl, GC_DCTL_RSV0_STATES, GC_EVB_DCTL_RSV0_STATES); |
914 | 916 | ||
915 | /* DLL reset done? */ | 917 | /* DLL reset done? */ |
916 | while ((inreg(dram_ctrl, GC_DCTL_RSV0_STATES) & GC_DCTL_STATES_MSK)) { | 918 | while ((inreg(dram_ctrl, GC_DCTL_RSV0_STATES) & GC_DCTL_STATES_MSK)) { |
917 | udelay(GC_DCTL_INIT_WAIT_INTERVAL); | 919 | udelay(GC_DCTL_INIT_WAIT_INTERVAL); |
918 | if (i++ > GC_DCTL_INIT_WAIT_CNT) { | 920 | if (i++ > GC_DCTL_INIT_WAIT_CNT) { |
919 | dev_err(par->dev, "VRAM init failed.\n"); | 921 | dev_err(par->dev, "VRAM init failed.\n"); |
920 | return -EINVAL; | 922 | return -EINVAL; |
921 | } | 923 | } |
922 | } | 924 | } |
923 | outreg(dram_ctrl, GC_DCTL_MODE_ADD, GC_EVB_DCTL_MODE_ADD_AFT_RST); | 925 | outreg(dram_ctrl, GC_DCTL_MODE_ADD, GC_EVB_DCTL_MODE_ADD_AFT_RST); |
924 | outreg(dram_ctrl, GC_DCTL_RSV0_STATES, GC_EVB_DCTL_RSV0_STATES_AFT_RST); | 926 | outreg(dram_ctrl, GC_DCTL_RSV0_STATES, GC_EVB_DCTL_RSV0_STATES_AFT_RST); |
925 | return 0; | 927 | return 0; |
926 | } | 928 | } |
927 | 929 | ||
928 | static int carmine_init(struct mb862xxfb_par *par) | 930 | static int carmine_init(struct mb862xxfb_par *par) |
929 | { | 931 | { |
930 | unsigned long reg; | 932 | unsigned long reg; |
931 | 933 | ||
932 | par->ctrl = par->mmio_base + MB86297_CTRL_BASE; | 934 | par->ctrl = par->mmio_base + MB86297_CTRL_BASE; |
933 | par->i2c = par->mmio_base + MB86297_I2C_BASE; | 935 | par->i2c = par->mmio_base + MB86297_I2C_BASE; |
934 | par->disp = par->mmio_base + MB86297_DISP0_BASE; | 936 | par->disp = par->mmio_base + MB86297_DISP0_BASE; |
935 | par->disp1 = par->mmio_base + MB86297_DISP1_BASE; | 937 | par->disp1 = par->mmio_base + MB86297_DISP1_BASE; |
936 | par->cap = par->mmio_base + MB86297_CAP0_BASE; | 938 | par->cap = par->mmio_base + MB86297_CAP0_BASE; |
937 | par->cap1 = par->mmio_base + MB86297_CAP1_BASE; | 939 | par->cap1 = par->mmio_base + MB86297_CAP1_BASE; |
938 | par->draw = par->mmio_base + MB86297_DRAW_BASE; | 940 | par->draw = par->mmio_base + MB86297_DRAW_BASE; |
939 | par->dram_ctrl = par->mmio_base + MB86297_DRAMCTRL_BASE; | 941 | par->dram_ctrl = par->mmio_base + MB86297_DRAMCTRL_BASE; |
940 | par->wrback = par->mmio_base + MB86297_WRBACK_BASE; | 942 | par->wrback = par->mmio_base + MB86297_WRBACK_BASE; |
941 | 943 | ||
942 | par->refclk = GC_DISP_REFCLK_533; | 944 | par->refclk = GC_DISP_REFCLK_533; |
943 | 945 | ||
944 | /* warm up */ | 946 | /* warm up */ |
945 | reg = GC_CTRL_CLK_EN_DRAM | GC_CTRL_CLK_EN_2D3D | GC_CTRL_CLK_EN_DISP0; | 947 | reg = GC_CTRL_CLK_EN_DRAM | GC_CTRL_CLK_EN_2D3D | GC_CTRL_CLK_EN_DISP0; |
946 | outreg(ctrl, GC_CTRL_CLK_ENABLE, reg); | 948 | outreg(ctrl, GC_CTRL_CLK_ENABLE, reg); |
947 | 949 | ||
948 | /* check for engine module revision */ | 950 | /* check for engine module revision */ |
949 | if (inreg(draw, GC_2D3D_REV) == GC_RE_REVISION) | 951 | if (inreg(draw, GC_2D3D_REV) == GC_RE_REVISION) |
950 | dev_info(par->dev, "Fujitsu Carmine GDC Rev.%d found\n", | 952 | dev_info(par->dev, "Fujitsu Carmine GDC Rev.%d found\n", |
951 | par->pdev->revision); | 953 | par->pdev->revision); |
952 | else | 954 | else |
953 | goto err_init; | 955 | goto err_init; |
954 | 956 | ||
955 | reg &= ~GC_CTRL_CLK_EN_2D3D; | 957 | reg &= ~GC_CTRL_CLK_EN_2D3D; |
956 | outreg(ctrl, GC_CTRL_CLK_ENABLE, reg); | 958 | outreg(ctrl, GC_CTRL_CLK_ENABLE, reg); |
957 | 959 | ||
958 | /* set up vram */ | 960 | /* set up vram */ |
959 | if (init_dram_ctrl(par) < 0) | 961 | if (init_dram_ctrl(par) < 0) |
960 | goto err_init; | 962 | goto err_init; |
961 | 963 | ||
962 | outreg(ctrl, GC_CTRL_INT_MASK, 0); | 964 | outreg(ctrl, GC_CTRL_INT_MASK, 0); |
963 | return 0; | 965 | return 0; |
964 | 966 | ||
965 | err_init: | 967 | err_init: |
966 | outreg(ctrl, GC_CTRL_CLK_ENABLE, 0); | 968 | outreg(ctrl, GC_CTRL_CLK_ENABLE, 0); |
967 | return -EINVAL; | 969 | return -EINVAL; |
968 | } | 970 | } |
969 | 971 | ||
970 | static inline int mb862xx_pci_gdc_init(struct mb862xxfb_par *par) | 972 | static inline int mb862xx_pci_gdc_init(struct mb862xxfb_par *par) |
971 | { | 973 | { |
972 | switch (par->type) { | 974 | switch (par->type) { |
973 | case BT_CORALP: | 975 | case BT_CORALP: |
974 | return coralp_init(par); | 976 | return coralp_init(par); |
975 | case BT_CARMINE: | 977 | case BT_CARMINE: |
976 | return carmine_init(par); | 978 | return carmine_init(par); |
977 | default: | 979 | default: |
978 | return -ENODEV; | 980 | return -ENODEV; |
979 | } | 981 | } |
980 | } | 982 | } |
981 | 983 | ||
982 | #define CHIP_ID(id) \ | 984 | #define CHIP_ID(id) \ |
983 | { PCI_DEVICE(PCI_VENDOR_ID_FUJITSU_LIMITED, id) } | 985 | { PCI_DEVICE(PCI_VENDOR_ID_FUJITSU_LIMITED, id) } |
984 | 986 | ||
985 | static struct pci_device_id mb862xx_pci_tbl[] __devinitdata = { | 987 | static struct pci_device_id mb862xx_pci_tbl[] __devinitdata = { |
986 | /* MB86295/MB86296 */ | 988 | /* MB86295/MB86296 */ |
987 | CHIP_ID(PCI_DEVICE_ID_FUJITSU_CORALP), | 989 | CHIP_ID(PCI_DEVICE_ID_FUJITSU_CORALP), |
988 | CHIP_ID(PCI_DEVICE_ID_FUJITSU_CORALPA), | 990 | CHIP_ID(PCI_DEVICE_ID_FUJITSU_CORALPA), |
989 | /* MB86297 */ | 991 | /* MB86297 */ |
990 | CHIP_ID(PCI_DEVICE_ID_FUJITSU_CARMINE), | 992 | CHIP_ID(PCI_DEVICE_ID_FUJITSU_CARMINE), |
991 | { 0, } | 993 | { 0, } |
992 | }; | 994 | }; |
993 | 995 | ||
994 | MODULE_DEVICE_TABLE(pci, mb862xx_pci_tbl); | 996 | MODULE_DEVICE_TABLE(pci, mb862xx_pci_tbl); |
995 | 997 | ||
996 | static int __devinit mb862xx_pci_probe(struct pci_dev *pdev, | 998 | static int __devinit mb862xx_pci_probe(struct pci_dev *pdev, |
997 | const struct pci_device_id *ent) | 999 | const struct pci_device_id *ent) |
998 | { | 1000 | { |
999 | struct mb862xxfb_par *par; | 1001 | struct mb862xxfb_par *par; |
1000 | struct fb_info *info; | 1002 | struct fb_info *info; |
1001 | struct device *dev = &pdev->dev; | 1003 | struct device *dev = &pdev->dev; |
1002 | int ret; | 1004 | int ret; |
1003 | 1005 | ||
1004 | ret = pci_enable_device(pdev); | 1006 | ret = pci_enable_device(pdev); |
1005 | if (ret < 0) { | 1007 | if (ret < 0) { |
1006 | dev_err(dev, "Cannot enable PCI device\n"); | 1008 | dev_err(dev, "Cannot enable PCI device\n"); |
1007 | goto out; | 1009 | goto out; |
1008 | } | 1010 | } |
1009 | 1011 | ||
1010 | info = framebuffer_alloc(sizeof(struct mb862xxfb_par), dev); | 1012 | info = framebuffer_alloc(sizeof(struct mb862xxfb_par), dev); |
1011 | if (!info) { | 1013 | if (!info) { |
1012 | dev_err(dev, "framebuffer alloc failed\n"); | 1014 | dev_err(dev, "framebuffer alloc failed\n"); |
1013 | ret = -ENOMEM; | 1015 | ret = -ENOMEM; |
1014 | goto dis_dev; | 1016 | goto dis_dev; |
1015 | } | 1017 | } |
1016 | 1018 | ||
1017 | par = info->par; | 1019 | par = info->par; |
1018 | par->info = info; | 1020 | par->info = info; |
1019 | par->dev = dev; | 1021 | par->dev = dev; |
1020 | par->pdev = pdev; | 1022 | par->pdev = pdev; |
1021 | par->irq = pdev->irq; | 1023 | par->irq = pdev->irq; |
1022 | 1024 | ||
1023 | ret = pci_request_regions(pdev, DRV_NAME); | 1025 | ret = pci_request_regions(pdev, DRV_NAME); |
1024 | if (ret < 0) { | 1026 | if (ret < 0) { |
1025 | dev_err(dev, "Cannot reserve region(s) for PCI device\n"); | 1027 | dev_err(dev, "Cannot reserve region(s) for PCI device\n"); |
1026 | goto rel_fb; | 1028 | goto rel_fb; |
1027 | } | 1029 | } |
1028 | 1030 | ||
1029 | switch (pdev->device) { | 1031 | switch (pdev->device) { |
1030 | case PCI_DEVICE_ID_FUJITSU_CORALP: | 1032 | case PCI_DEVICE_ID_FUJITSU_CORALP: |
1031 | case PCI_DEVICE_ID_FUJITSU_CORALPA: | 1033 | case PCI_DEVICE_ID_FUJITSU_CORALPA: |
1032 | par->fb_base_phys = pci_resource_start(par->pdev, 0); | 1034 | par->fb_base_phys = pci_resource_start(par->pdev, 0); |
1033 | par->mapped_vram = CORALP_MEM_SIZE; | 1035 | par->mapped_vram = CORALP_MEM_SIZE; |
1034 | if (par->mapped_vram >= 0x2000000) { | 1036 | if (par->mapped_vram >= 0x2000000) { |
1035 | par->mmio_base_phys = par->fb_base_phys + | 1037 | par->mmio_base_phys = par->fb_base_phys + |
1036 | MB862XX_MMIO_HIGH_BASE; | 1038 | MB862XX_MMIO_HIGH_BASE; |
1037 | } else { | 1039 | } else { |
1038 | par->mmio_base_phys = par->fb_base_phys + | 1040 | par->mmio_base_phys = par->fb_base_phys + |
1039 | MB862XX_MMIO_BASE; | 1041 | MB862XX_MMIO_BASE; |
1040 | } | 1042 | } |
1041 | par->mmio_len = MB862XX_MMIO_SIZE; | 1043 | par->mmio_len = MB862XX_MMIO_SIZE; |
1042 | par->type = BT_CORALP; | 1044 | par->type = BT_CORALP; |
1043 | break; | 1045 | break; |
1044 | case PCI_DEVICE_ID_FUJITSU_CARMINE: | 1046 | case PCI_DEVICE_ID_FUJITSU_CARMINE: |
1045 | par->fb_base_phys = pci_resource_start(par->pdev, 2); | 1047 | par->fb_base_phys = pci_resource_start(par->pdev, 2); |
1046 | par->mmio_base_phys = pci_resource_start(par->pdev, 3); | 1048 | par->mmio_base_phys = pci_resource_start(par->pdev, 3); |
1047 | par->mmio_len = pci_resource_len(par->pdev, 3); | 1049 | par->mmio_len = pci_resource_len(par->pdev, 3); |
1048 | par->mapped_vram = CARMINE_MEM_SIZE; | 1050 | par->mapped_vram = CARMINE_MEM_SIZE; |
1049 | par->type = BT_CARMINE; | 1051 | par->type = BT_CARMINE; |
1050 | break; | 1052 | break; |
1051 | default: | 1053 | default: |
1052 | /* should never occur */ | 1054 | /* should never occur */ |
1053 | goto rel_reg; | 1055 | goto rel_reg; |
1054 | } | 1056 | } |
1055 | 1057 | ||
1056 | par->fb_base = ioremap(par->fb_base_phys, par->mapped_vram); | 1058 | par->fb_base = ioremap(par->fb_base_phys, par->mapped_vram); |
1057 | if (par->fb_base == NULL) { | 1059 | if (par->fb_base == NULL) { |
1058 | dev_err(dev, "Cannot map framebuffer\n"); | 1060 | dev_err(dev, "Cannot map framebuffer\n"); |
1059 | goto rel_reg; | 1061 | goto rel_reg; |
1060 | } | 1062 | } |
1061 | 1063 | ||
1062 | par->mmio_base = ioremap(par->mmio_base_phys, par->mmio_len); | 1064 | par->mmio_base = ioremap(par->mmio_base_phys, par->mmio_len); |
1063 | if (par->mmio_base == NULL) { | 1065 | if (par->mmio_base == NULL) { |
1064 | dev_err(dev, "Cannot map registers\n"); | 1066 | dev_err(dev, "Cannot map registers\n"); |
1065 | ret = -EIO; | 1067 | ret = -EIO; |
1066 | goto fb_unmap; | 1068 | goto fb_unmap; |
1067 | } | 1069 | } |
1068 | 1070 | ||
1069 | dev_dbg(dev, "fb phys 0x%llx 0x%lx\n", | 1071 | dev_dbg(dev, "fb phys 0x%llx 0x%lx\n", |
1070 | (unsigned long long)par->fb_base_phys, (ulong)par->mapped_vram); | 1072 | (unsigned long long)par->fb_base_phys, (ulong)par->mapped_vram); |
1071 | dev_dbg(dev, "mmio phys 0x%llx 0x%lx\n", | 1073 | dev_dbg(dev, "mmio phys 0x%llx 0x%lx\n", |
1072 | (unsigned long long)par->mmio_base_phys, (ulong)par->mmio_len); | 1074 | (unsigned long long)par->mmio_base_phys, (ulong)par->mmio_len); |
1073 | 1075 | ||
1074 | if (mb862xx_pci_gdc_init(par)) | 1076 | if (mb862xx_pci_gdc_init(par)) |
1075 | goto io_unmap; | 1077 | goto io_unmap; |
1076 | 1078 | ||
1077 | if (request_irq(par->irq, mb862xx_intr, IRQF_SHARED, | 1079 | if (request_irq(par->irq, mb862xx_intr, IRQF_SHARED, |
1078 | DRV_NAME, (void *)par)) { | 1080 | DRV_NAME, (void *)par)) { |
1079 | dev_err(dev, "Cannot request irq\n"); | 1081 | dev_err(dev, "Cannot request irq\n"); |
1080 | goto io_unmap; | 1082 | goto io_unmap; |
1081 | } | 1083 | } |
1082 | 1084 | ||
1083 | mb862xxfb_init_fbinfo(info); | 1085 | mb862xxfb_init_fbinfo(info); |
1084 | 1086 | ||
1085 | if (fb_alloc_cmap(&info->cmap, NR_PALETTE, 0) < 0) { | 1087 | if (fb_alloc_cmap(&info->cmap, NR_PALETTE, 0) < 0) { |
1086 | dev_err(dev, "Could not allocate cmap for fb_info.\n"); | 1088 | dev_err(dev, "Could not allocate cmap for fb_info.\n"); |
1087 | ret = -ENOMEM; | 1089 | ret = -ENOMEM; |
1088 | goto free_irq; | 1090 | goto free_irq; |
1089 | } | 1091 | } |
1090 | 1092 | ||
1091 | if ((info->fbops->fb_set_par)(info)) | 1093 | if ((info->fbops->fb_set_par)(info)) |
1092 | dev_err(dev, "set_var() failed on initial setup?\n"); | 1094 | dev_err(dev, "set_var() failed on initial setup?\n"); |
1093 | 1095 | ||
1094 | ret = register_framebuffer(info); | 1096 | ret = register_framebuffer(info); |
1095 | if (ret < 0) { | 1097 | if (ret < 0) { |
1096 | dev_err(dev, "failed to register framebuffer\n"); | 1098 | dev_err(dev, "failed to register framebuffer\n"); |
1097 | goto rel_cmap; | 1099 | goto rel_cmap; |
1098 | } | 1100 | } |
1099 | 1101 | ||
1100 | pci_set_drvdata(pdev, info); | 1102 | pci_set_drvdata(pdev, info); |
1101 | 1103 | ||
1102 | if (device_create_file(dev, &dev_attr_dispregs)) | 1104 | if (device_create_file(dev, &dev_attr_dispregs)) |
1103 | dev_err(dev, "Can't create sysfs regdump file\n"); | 1105 | dev_err(dev, "Can't create sysfs regdump file\n"); |
1104 | 1106 | ||
1105 | if (par->type == BT_CARMINE) | 1107 | if (par->type == BT_CARMINE) |
1106 | outreg(ctrl, GC_CTRL_INT_MASK, GC_CARMINE_INT_EN); | 1108 | outreg(ctrl, GC_CTRL_INT_MASK, GC_CARMINE_INT_EN); |
1107 | else | 1109 | else |
1108 | outreg(host, GC_IMASK, GC_INT_EN); | 1110 | outreg(host, GC_IMASK, GC_INT_EN); |
1109 | 1111 | ||
1110 | return 0; | 1112 | return 0; |
1111 | 1113 | ||
1112 | rel_cmap: | 1114 | rel_cmap: |
1113 | fb_dealloc_cmap(&info->cmap); | 1115 | fb_dealloc_cmap(&info->cmap); |
1114 | free_irq: | 1116 | free_irq: |
1115 | free_irq(par->irq, (void *)par); | 1117 | free_irq(par->irq, (void *)par); |
1116 | io_unmap: | 1118 | io_unmap: |
1117 | iounmap(par->mmio_base); | 1119 | iounmap(par->mmio_base); |
1118 | fb_unmap: | 1120 | fb_unmap: |
1119 | iounmap(par->fb_base); | 1121 | iounmap(par->fb_base); |
1120 | rel_reg: | 1122 | rel_reg: |
1121 | pci_release_regions(pdev); | 1123 | pci_release_regions(pdev); |
1122 | rel_fb: | 1124 | rel_fb: |
1123 | framebuffer_release(info); | 1125 | framebuffer_release(info); |
1124 | dis_dev: | 1126 | dis_dev: |
1125 | pci_disable_device(pdev); | 1127 | pci_disable_device(pdev); |
1126 | out: | 1128 | out: |
1127 | return ret; | 1129 | return ret; |
1128 | } | 1130 | } |
1129 | 1131 | ||
1130 | static void __devexit mb862xx_pci_remove(struct pci_dev *pdev) | 1132 | static void __devexit mb862xx_pci_remove(struct pci_dev *pdev) |
1131 | { | 1133 | { |
1132 | struct fb_info *fbi = pci_get_drvdata(pdev); | 1134 | struct fb_info *fbi = pci_get_drvdata(pdev); |
1133 | struct mb862xxfb_par *par = fbi->par; | 1135 | struct mb862xxfb_par *par = fbi->par; |
1134 | unsigned long reg; | 1136 | unsigned long reg; |
1135 | 1137 | ||
1136 | dev_dbg(fbi->dev, "%s release\n", fbi->fix.id); | 1138 | dev_dbg(fbi->dev, "%s release\n", fbi->fix.id); |
1137 | 1139 | ||
1138 | /* display off */ | 1140 | /* display off */ |
1139 | reg = inreg(disp, GC_DCM1); | 1141 | reg = inreg(disp, GC_DCM1); |
1140 | reg &= ~(GC_DCM01_DEN | GC_DCM01_L0E); | 1142 | reg &= ~(GC_DCM01_DEN | GC_DCM01_L0E); |
1141 | outreg(disp, GC_DCM1, reg); | 1143 | outreg(disp, GC_DCM1, reg); |
1142 | 1144 | ||
1143 | if (par->type == BT_CARMINE) { | 1145 | if (par->type == BT_CARMINE) { |
1144 | outreg(ctrl, GC_CTRL_INT_MASK, 0); | 1146 | outreg(ctrl, GC_CTRL_INT_MASK, 0); |
1145 | outreg(ctrl, GC_CTRL_CLK_ENABLE, 0); | 1147 | outreg(ctrl, GC_CTRL_CLK_ENABLE, 0); |
1146 | } else { | 1148 | } else { |
1147 | outreg(host, GC_IMASK, 0); | 1149 | outreg(host, GC_IMASK, 0); |
1148 | } | 1150 | } |
1149 | 1151 | ||
1150 | mb862xx_i2c_exit(par); | 1152 | mb862xx_i2c_exit(par); |
1151 | 1153 | ||
1152 | device_remove_file(&pdev->dev, &dev_attr_dispregs); | 1154 | device_remove_file(&pdev->dev, &dev_attr_dispregs); |
1153 | 1155 | ||
1154 | pci_set_drvdata(pdev, NULL); | 1156 | pci_set_drvdata(pdev, NULL); |
1155 | unregister_framebuffer(fbi); | 1157 | unregister_framebuffer(fbi); |
1156 | fb_dealloc_cmap(&fbi->cmap); | 1158 | fb_dealloc_cmap(&fbi->cmap); |
1157 | 1159 | ||
1158 | free_irq(par->irq, (void *)par); | 1160 | free_irq(par->irq, (void *)par); |
1159 | iounmap(par->mmio_base); | 1161 | iounmap(par->mmio_base); |
1160 | iounmap(par->fb_base); | 1162 | iounmap(par->fb_base); |
1161 | 1163 | ||
1162 | pci_release_regions(pdev); | 1164 | pci_release_regions(pdev); |
1163 | framebuffer_release(fbi); | 1165 | framebuffer_release(fbi); |
1164 | pci_disable_device(pdev); | 1166 | pci_disable_device(pdev); |
1165 | } | 1167 | } |
1166 | 1168 | ||
1167 | static struct pci_driver mb862xxfb_pci_driver = { | 1169 | static struct pci_driver mb862xxfb_pci_driver = { |
1168 | .name = DRV_NAME, | 1170 | .name = DRV_NAME, |
1169 | .id_table = mb862xx_pci_tbl, | 1171 | .id_table = mb862xx_pci_tbl, |
1170 | .probe = mb862xx_pci_probe, | 1172 | .probe = mb862xx_pci_probe, |
1171 | .remove = __devexit_p(mb862xx_pci_remove), | 1173 | .remove = __devexit_p(mb862xx_pci_remove), |
1172 | }; | 1174 | }; |
1173 | #endif | 1175 | #endif |
1174 | 1176 | ||
1175 | static int __devinit mb862xxfb_init(void) | 1177 | static int __devinit mb862xxfb_init(void) |
1176 | { | 1178 | { |
1177 | int ret = -ENODEV; | 1179 | int ret = -ENODEV; |
1178 | 1180 | ||
1179 | #if defined(CONFIG_FB_MB862XX_LIME) | 1181 | #if defined(CONFIG_FB_MB862XX_LIME) |
1180 | ret = platform_driver_register(&of_platform_mb862xxfb_driver); | 1182 | ret = platform_driver_register(&of_platform_mb862xxfb_driver); |
1181 | #endif | 1183 | #endif |
1182 | #if defined(CONFIG_FB_MB862XX_PCI_GDC) | 1184 | #if defined(CONFIG_FB_MB862XX_PCI_GDC) |
1183 | ret = pci_register_driver(&mb862xxfb_pci_driver); | 1185 | ret = pci_register_driver(&mb862xxfb_pci_driver); |
1184 | #endif | 1186 | #endif |
1185 | return ret; | 1187 | return ret; |
1186 | } | 1188 | } |
1187 | 1189 | ||
1188 | static void __exit mb862xxfb_exit(void) | 1190 | static void __exit mb862xxfb_exit(void) |
1189 | { | 1191 | { |
1190 | #if defined(CONFIG_FB_MB862XX_LIME) | 1192 | #if defined(CONFIG_FB_MB862XX_LIME) |
1191 | platform_driver_unregister(&of_platform_mb862xxfb_driver); | 1193 | platform_driver_unregister(&of_platform_mb862xxfb_driver); |
1192 | #endif | 1194 | #endif |
1193 | #if defined(CONFIG_FB_MB862XX_PCI_GDC) | 1195 | #if defined(CONFIG_FB_MB862XX_PCI_GDC) |
1194 | pci_unregister_driver(&mb862xxfb_pci_driver); | 1196 | pci_unregister_driver(&mb862xxfb_pci_driver); |
1195 | #endif | 1197 | #endif |
1196 | } | 1198 | } |
1197 | 1199 | ||
1198 | module_init(mb862xxfb_init); | 1200 | module_init(mb862xxfb_init); |
1199 | module_exit(mb862xxfb_exit); | 1201 | module_exit(mb862xxfb_exit); |
1200 | 1202 | ||
1201 | MODULE_DESCRIPTION("Fujitsu MB862xx Framebuffer driver"); | 1203 | MODULE_DESCRIPTION("Fujitsu MB862xx Framebuffer driver"); |
1202 | MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>"); | 1204 | MODULE_AUTHOR("Anatolij Gustschin <agust@denx.de>"); |
1203 | MODULE_LICENSE("GPL v2"); | 1205 | MODULE_LICENSE("GPL v2"); |
1204 | 1206 |
drivers/video/omap2/dss/sdi.c
1 | /* | 1 | /* |
2 | * linux/drivers/video/omap2/dss/sdi.c | 2 | * linux/drivers/video/omap2/dss/sdi.c |
3 | * | 3 | * |
4 | * Copyright (C) 2009 Nokia Corporation | 4 | * Copyright (C) 2009 Nokia Corporation |
5 | * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | 5 | * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
6 | * | 6 | * |
7 | * This program is free software; you can redistribute it and/or modify it | 7 | * This program is free software; you can redistribute it and/or modify it |
8 | * under the terms of the GNU General Public License version 2 as published by | 8 | * under the terms of the GNU General Public License version 2 as published by |
9 | * the Free Software Foundation. | 9 | * the Free Software Foundation. |
10 | * | 10 | * |
11 | * This program is distributed in the hope that it will be useful, but WITHOUT | 11 | * This program is distributed in the hope that it will be useful, but WITHOUT |
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | 13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
14 | * more details. | 14 | * more details. |
15 | * | 15 | * |
16 | * You should have received a copy of the GNU General Public License along with | 16 | * You should have received a copy of the GNU General Public License along with |
17 | * this program. If not, see <http://www.gnu.org/licenses/>. | 17 | * this program. If not, see <http://www.gnu.org/licenses/>. |
18 | */ | 18 | */ |
19 | 19 | ||
20 | #define DSS_SUBSYS_NAME "SDI" | 20 | #define DSS_SUBSYS_NAME "SDI" |
21 | 21 | ||
22 | #include <linux/kernel.h> | 22 | #include <linux/kernel.h> |
23 | #include <linux/delay.h> | 23 | #include <linux/delay.h> |
24 | #include <linux/err.h> | 24 | #include <linux/err.h> |
25 | #include <linux/regulator/consumer.h> | 25 | #include <linux/regulator/consumer.h> |
26 | #include <linux/export.h> | 26 | #include <linux/export.h> |
27 | #include <linux/platform_device.h> | 27 | #include <linux/platform_device.h> |
28 | 28 | ||
29 | #include <video/omapdss.h> | 29 | #include <video/omapdss.h> |
30 | #include "dss.h" | 30 | #include "dss.h" |
31 | 31 | ||
32 | static struct { | 32 | static struct { |
33 | bool update_enabled; | 33 | bool update_enabled; |
34 | struct regulator *vdds_sdi_reg; | 34 | struct regulator *vdds_sdi_reg; |
35 | 35 | ||
36 | struct dss_lcd_mgr_config mgr_config; | 36 | struct dss_lcd_mgr_config mgr_config; |
37 | } sdi; | 37 | } sdi; |
38 | 38 | ||
39 | static void sdi_config_lcd_manager(struct omap_dss_device *dssdev) | 39 | static void sdi_config_lcd_manager(struct omap_dss_device *dssdev) |
40 | { | 40 | { |
41 | sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; | 41 | sdi.mgr_config.io_pad_mode = DSS_IO_PAD_MODE_BYPASS; |
42 | 42 | ||
43 | sdi.mgr_config.stallmode = false; | 43 | sdi.mgr_config.stallmode = false; |
44 | sdi.mgr_config.fifohandcheck = false; | 44 | sdi.mgr_config.fifohandcheck = false; |
45 | 45 | ||
46 | sdi.mgr_config.video_port_width = 24; | 46 | sdi.mgr_config.video_port_width = 24; |
47 | sdi.mgr_config.lcden_sig_polarity = 1; | 47 | sdi.mgr_config.lcden_sig_polarity = 1; |
48 | 48 | ||
49 | dss_mgr_set_lcd_config(dssdev->manager, &sdi.mgr_config); | 49 | dss_mgr_set_lcd_config(dssdev->manager, &sdi.mgr_config); |
50 | } | 50 | } |
51 | 51 | ||
52 | int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) | 52 | int omapdss_sdi_display_enable(struct omap_dss_device *dssdev) |
53 | { | 53 | { |
54 | struct omap_video_timings *t = &dssdev->panel.timings; | 54 | struct omap_video_timings *t = &dssdev->panel.timings; |
55 | struct dss_clock_info dss_cinfo; | 55 | struct dss_clock_info dss_cinfo; |
56 | struct dispc_clock_info dispc_cinfo; | 56 | struct dispc_clock_info dispc_cinfo; |
57 | unsigned long pck; | 57 | unsigned long pck; |
58 | int r; | 58 | int r; |
59 | 59 | ||
60 | if (dssdev->manager == NULL) { | 60 | if (dssdev->manager == NULL) { |
61 | DSSERR("failed to enable display: no manager\n"); | 61 | DSSERR("failed to enable display: no manager\n"); |
62 | return -ENODEV; | 62 | return -ENODEV; |
63 | } | 63 | } |
64 | 64 | ||
65 | r = omap_dss_start_device(dssdev); | 65 | r = omap_dss_start_device(dssdev); |
66 | if (r) { | 66 | if (r) { |
67 | DSSERR("failed to start device\n"); | 67 | DSSERR("failed to start device\n"); |
68 | goto err_start_dev; | 68 | goto err_start_dev; |
69 | } | 69 | } |
70 | 70 | ||
71 | r = regulator_enable(sdi.vdds_sdi_reg); | 71 | r = regulator_enable(sdi.vdds_sdi_reg); |
72 | if (r) | 72 | if (r) |
73 | goto err_reg_enable; | 73 | goto err_reg_enable; |
74 | 74 | ||
75 | r = dispc_runtime_get(); | 75 | r = dispc_runtime_get(); |
76 | if (r) | 76 | if (r) |
77 | goto err_get_dispc; | 77 | goto err_get_dispc; |
78 | 78 | ||
79 | /* 15.5.9.1.2 */ | 79 | /* 15.5.9.1.2 */ |
80 | dssdev->panel.timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; | 80 | dssdev->panel.timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; |
81 | dssdev->panel.timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; | 81 | dssdev->panel.timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; |
82 | 82 | ||
83 | r = dss_calc_clock_div(t->pixel_clock * 1000, &dss_cinfo, &dispc_cinfo); | 83 | r = dss_calc_clock_div(t->pixel_clock * 1000, &dss_cinfo, &dispc_cinfo); |
84 | if (r) | 84 | if (r) |
85 | goto err_calc_clock_div; | 85 | goto err_calc_clock_div; |
86 | 86 | ||
87 | sdi.mgr_config.clock_info = dispc_cinfo; | 87 | sdi.mgr_config.clock_info = dispc_cinfo; |
88 | 88 | ||
89 | pck = dss_cinfo.fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div / 1000; | 89 | pck = dss_cinfo.fck / dispc_cinfo.lck_div / dispc_cinfo.pck_div / 1000; |
90 | 90 | ||
91 | if (pck != t->pixel_clock) { | 91 | if (pck != t->pixel_clock) { |
92 | DSSWARN("Could not find exact pixel clock. Requested %d kHz, " | 92 | DSSWARN("Could not find exact pixel clock. Requested %d kHz, " |
93 | "got %lu kHz\n", | 93 | "got %lu kHz\n", |
94 | t->pixel_clock, pck); | 94 | t->pixel_clock, pck); |
95 | 95 | ||
96 | t->pixel_clock = pck; | 96 | t->pixel_clock = pck; |
97 | } | 97 | } |
98 | 98 | ||
99 | 99 | ||
100 | dss_mgr_set_timings(dssdev->manager, t); | 100 | dss_mgr_set_timings(dssdev->manager, t); |
101 | 101 | ||
102 | r = dss_set_clock_div(&dss_cinfo); | 102 | r = dss_set_clock_div(&dss_cinfo); |
103 | if (r) | 103 | if (r) |
104 | goto err_set_dss_clock_div; | 104 | goto err_set_dss_clock_div; |
105 | 105 | ||
106 | sdi_config_lcd_manager(dssdev); | 106 | sdi_config_lcd_manager(dssdev); |
107 | 107 | ||
108 | /* | ||
109 | * LCLK and PCLK divisors are located in shadow registers, and we | ||
110 | * normally write them to DISPC registers when enabling the output. | ||
111 | * However, SDI uses pck-free as source clock for its PLL, and pck-free | ||
112 | * is affected by the divisors. And as we need the PLL before enabling | ||
113 | * the output, we need to write the divisors early. | ||
114 | * | ||
115 | * It seems just writing to the DISPC register is enough, and we don't | ||
116 | * need to care about the shadow register mechanism for pck-free. The | ||
117 | * exact reason for this is unknown. | ||
118 | */ | ||
119 | dispc_mgr_set_clock_div(dssdev->manager->id, | ||
120 | &sdi.mgr_config.clock_info); | ||
121 | |||
108 | dss_sdi_init(dssdev->phy.sdi.datapairs); | 122 | dss_sdi_init(dssdev->phy.sdi.datapairs); |
109 | r = dss_sdi_enable(); | 123 | r = dss_sdi_enable(); |
110 | if (r) | 124 | if (r) |
111 | goto err_sdi_enable; | 125 | goto err_sdi_enable; |
112 | mdelay(2); | 126 | mdelay(2); |
113 | 127 | ||
114 | r = dss_mgr_enable(dssdev->manager); | 128 | r = dss_mgr_enable(dssdev->manager); |
115 | if (r) | 129 | if (r) |
116 | goto err_mgr_enable; | 130 | goto err_mgr_enable; |
117 | 131 | ||
118 | return 0; | 132 | return 0; |
119 | 133 | ||
120 | err_mgr_enable: | 134 | err_mgr_enable: |
121 | dss_sdi_disable(); | 135 | dss_sdi_disable(); |
122 | err_sdi_enable: | 136 | err_sdi_enable: |
123 | err_set_dss_clock_div: | 137 | err_set_dss_clock_div: |
124 | err_calc_clock_div: | 138 | err_calc_clock_div: |
125 | dispc_runtime_put(); | 139 | dispc_runtime_put(); |
126 | err_get_dispc: | 140 | err_get_dispc: |
127 | regulator_disable(sdi.vdds_sdi_reg); | 141 | regulator_disable(sdi.vdds_sdi_reg); |
128 | err_reg_enable: | 142 | err_reg_enable: |
129 | omap_dss_stop_device(dssdev); | 143 | omap_dss_stop_device(dssdev); |
130 | err_start_dev: | 144 | err_start_dev: |
131 | return r; | 145 | return r; |
132 | } | 146 | } |
133 | EXPORT_SYMBOL(omapdss_sdi_display_enable); | 147 | EXPORT_SYMBOL(omapdss_sdi_display_enable); |
134 | 148 | ||
135 | void omapdss_sdi_display_disable(struct omap_dss_device *dssdev) | 149 | void omapdss_sdi_display_disable(struct omap_dss_device *dssdev) |
136 | { | 150 | { |
137 | dss_mgr_disable(dssdev->manager); | 151 | dss_mgr_disable(dssdev->manager); |
138 | 152 | ||
139 | dss_sdi_disable(); | 153 | dss_sdi_disable(); |
140 | 154 | ||
141 | dispc_runtime_put(); | 155 | dispc_runtime_put(); |
142 | 156 | ||
143 | regulator_disable(sdi.vdds_sdi_reg); | 157 | regulator_disable(sdi.vdds_sdi_reg); |
144 | 158 | ||
145 | omap_dss_stop_device(dssdev); | 159 | omap_dss_stop_device(dssdev); |
146 | } | 160 | } |
147 | EXPORT_SYMBOL(omapdss_sdi_display_disable); | 161 | EXPORT_SYMBOL(omapdss_sdi_display_disable); |
148 | 162 | ||
149 | static int __init sdi_init_display(struct omap_dss_device *dssdev) | 163 | static int __init sdi_init_display(struct omap_dss_device *dssdev) |
150 | { | 164 | { |
151 | DSSDBG("SDI init\n"); | 165 | DSSDBG("SDI init\n"); |
152 | 166 | ||
153 | if (sdi.vdds_sdi_reg == NULL) { | 167 | if (sdi.vdds_sdi_reg == NULL) { |
154 | struct regulator *vdds_sdi; | 168 | struct regulator *vdds_sdi; |
155 | 169 | ||
156 | vdds_sdi = dss_get_vdds_sdi(); | 170 | vdds_sdi = dss_get_vdds_sdi(); |
157 | 171 | ||
158 | if (IS_ERR(vdds_sdi)) { | 172 | if (IS_ERR(vdds_sdi)) { |
159 | DSSERR("can't get VDDS_SDI regulator\n"); | 173 | DSSERR("can't get VDDS_SDI regulator\n"); |
160 | return PTR_ERR(vdds_sdi); | 174 | return PTR_ERR(vdds_sdi); |
161 | } | 175 | } |
162 | 176 | ||
163 | sdi.vdds_sdi_reg = vdds_sdi; | 177 | sdi.vdds_sdi_reg = vdds_sdi; |
164 | } | 178 | } |
165 | 179 | ||
166 | return 0; | 180 | return 0; |
167 | } | 181 | } |
168 | 182 | ||
169 | static void __init sdi_probe_pdata(struct platform_device *pdev) | 183 | static void __init sdi_probe_pdata(struct platform_device *pdev) |
170 | { | 184 | { |
171 | struct omap_dss_board_info *pdata = pdev->dev.platform_data; | 185 | struct omap_dss_board_info *pdata = pdev->dev.platform_data; |
172 | int i, r; | 186 | int i, r; |
173 | 187 | ||
174 | for (i = 0; i < pdata->num_devices; ++i) { | 188 | for (i = 0; i < pdata->num_devices; ++i) { |
175 | struct omap_dss_device *dssdev = pdata->devices[i]; | 189 | struct omap_dss_device *dssdev = pdata->devices[i]; |
176 | 190 | ||
177 | if (dssdev->type != OMAP_DISPLAY_TYPE_SDI) | 191 | if (dssdev->type != OMAP_DISPLAY_TYPE_SDI) |
178 | continue; | 192 | continue; |
179 | 193 | ||
180 | r = sdi_init_display(dssdev); | 194 | r = sdi_init_display(dssdev); |
181 | if (r) { | 195 | if (r) { |
182 | DSSERR("device %s init failed: %d\n", dssdev->name, r); | 196 | DSSERR("device %s init failed: %d\n", dssdev->name, r); |
183 | continue; | 197 | continue; |
184 | } | 198 | } |
185 | 199 | ||
186 | r = omap_dss_register_device(dssdev, &pdev->dev, i); | 200 | r = omap_dss_register_device(dssdev, &pdev->dev, i); |
187 | if (r) | 201 | if (r) |
188 | DSSERR("device %s register failed: %d\n", | 202 | DSSERR("device %s register failed: %d\n", |
189 | dssdev->name, r); | 203 | dssdev->name, r); |
190 | } | 204 | } |
191 | } | 205 | } |
192 | 206 | ||
193 | static int __init omap_sdi_probe(struct platform_device *pdev) | 207 | static int __init omap_sdi_probe(struct platform_device *pdev) |
194 | { | 208 | { |
195 | sdi_probe_pdata(pdev); | 209 | sdi_probe_pdata(pdev); |
196 | 210 | ||
197 | return 0; | 211 | return 0; |
198 | } | 212 | } |
199 | 213 | ||
200 | static int __exit omap_sdi_remove(struct platform_device *pdev) | 214 | static int __exit omap_sdi_remove(struct platform_device *pdev) |
201 | { | 215 | { |
202 | omap_dss_unregister_child_devices(&pdev->dev); | 216 | omap_dss_unregister_child_devices(&pdev->dev); |
203 | 217 | ||
204 | return 0; | 218 | return 0; |
205 | } | 219 | } |
206 | 220 | ||
207 | static struct platform_driver omap_sdi_driver = { | 221 | static struct platform_driver omap_sdi_driver = { |
208 | .remove = __exit_p(omap_sdi_remove), | 222 | .remove = __exit_p(omap_sdi_remove), |
209 | .driver = { | 223 | .driver = { |
210 | .name = "omapdss_sdi", | 224 | .name = "omapdss_sdi", |
211 | .owner = THIS_MODULE, | 225 | .owner = THIS_MODULE, |
212 | }, | 226 | }, |
213 | }; | 227 | }; |
214 | 228 | ||
215 | int __init sdi_init_platform_driver(void) | 229 | int __init sdi_init_platform_driver(void) |
216 | { | 230 | { |
217 | return platform_driver_probe(&omap_sdi_driver, omap_sdi_probe); | 231 | return platform_driver_probe(&omap_sdi_driver, omap_sdi_probe); |
218 | } | 232 | } |
219 | 233 | ||
220 | void __exit sdi_uninit_platform_driver(void) | 234 | void __exit sdi_uninit_platform_driver(void) |
221 | { | 235 | { |
222 | platform_driver_unregister(&omap_sdi_driver); | 236 | platform_driver_unregister(&omap_sdi_driver); |
223 | } | 237 | } |
224 | 238 |
drivers/video/omap2/omapfb/omapfb-main.c
1 | /* | 1 | /* |
2 | * linux/drivers/video/omap2/omapfb-main.c | 2 | * linux/drivers/video/omap2/omapfb-main.c |
3 | * | 3 | * |
4 | * Copyright (C) 2008 Nokia Corporation | 4 | * Copyright (C) 2008 Nokia Corporation |
5 | * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | 5 | * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> |
6 | * | 6 | * |
7 | * Some code and ideas taken from drivers/video/omap/ driver | 7 | * Some code and ideas taken from drivers/video/omap/ driver |
8 | * by Imre Deak. | 8 | * by Imre Deak. |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify it | 10 | * This program is free software; you can redistribute it and/or modify it |
11 | * under the terms of the GNU General Public License version 2 as published by | 11 | * under the terms of the GNU General Public License version 2 as published by |
12 | * the Free Software Foundation. | 12 | * the Free Software Foundation. |
13 | * | 13 | * |
14 | * This program is distributed in the hope that it will be useful, but WITHOUT | 14 | * This program is distributed in the hope that it will be useful, but WITHOUT |
15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 15 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | 16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for |
17 | * more details. | 17 | * more details. |
18 | * | 18 | * |
19 | * You should have received a copy of the GNU General Public License along with | 19 | * You should have received a copy of the GNU General Public License along with |
20 | * this program. If not, see <http://www.gnu.org/licenses/>. | 20 | * this program. If not, see <http://www.gnu.org/licenses/>. |
21 | */ | 21 | */ |
22 | 22 | ||
23 | #include <linux/module.h> | 23 | #include <linux/module.h> |
24 | #include <linux/delay.h> | 24 | #include <linux/delay.h> |
25 | #include <linux/slab.h> | 25 | #include <linux/slab.h> |
26 | #include <linux/fb.h> | 26 | #include <linux/fb.h> |
27 | #include <linux/dma-mapping.h> | 27 | #include <linux/dma-mapping.h> |
28 | #include <linux/vmalloc.h> | 28 | #include <linux/vmalloc.h> |
29 | #include <linux/device.h> | 29 | #include <linux/device.h> |
30 | #include <linux/platform_device.h> | 30 | #include <linux/platform_device.h> |
31 | #include <linux/omapfb.h> | 31 | #include <linux/omapfb.h> |
32 | 32 | ||
33 | #include <video/omapdss.h> | 33 | #include <video/omapdss.h> |
34 | #include <plat/vram.h> | 34 | #include <plat/vram.h> |
35 | #include <plat/vrfb.h> | 35 | #include <plat/vrfb.h> |
36 | 36 | ||
37 | #include "omapfb.h" | 37 | #include "omapfb.h" |
38 | 38 | ||
39 | #define MODULE_NAME "omapfb" | 39 | #define MODULE_NAME "omapfb" |
40 | 40 | ||
41 | #define OMAPFB_PLANE_XRES_MIN 8 | 41 | #define OMAPFB_PLANE_XRES_MIN 8 |
42 | #define OMAPFB_PLANE_YRES_MIN 8 | 42 | #define OMAPFB_PLANE_YRES_MIN 8 |
43 | 43 | ||
44 | static char *def_mode; | 44 | static char *def_mode; |
45 | static char *def_vram; | 45 | static char *def_vram; |
46 | static bool def_vrfb; | 46 | static bool def_vrfb; |
47 | static int def_rotate; | 47 | static int def_rotate; |
48 | static bool def_mirror; | 48 | static bool def_mirror; |
49 | static bool auto_update; | 49 | static bool auto_update; |
50 | static unsigned int auto_update_freq; | 50 | static unsigned int auto_update_freq; |
51 | module_param(auto_update, bool, 0); | 51 | module_param(auto_update, bool, 0); |
52 | module_param(auto_update_freq, uint, 0644); | 52 | module_param(auto_update_freq, uint, 0644); |
53 | 53 | ||
54 | #ifdef DEBUG | 54 | #ifdef DEBUG |
55 | bool omapfb_debug; | 55 | bool omapfb_debug; |
56 | module_param_named(debug, omapfb_debug, bool, 0644); | 56 | module_param_named(debug, omapfb_debug, bool, 0644); |
57 | static bool omapfb_test_pattern; | 57 | static bool omapfb_test_pattern; |
58 | module_param_named(test, omapfb_test_pattern, bool, 0644); | 58 | module_param_named(test, omapfb_test_pattern, bool, 0644); |
59 | #endif | 59 | #endif |
60 | 60 | ||
61 | static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi); | 61 | static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi); |
62 | static int omapfb_get_recommended_bpp(struct omapfb2_device *fbdev, | 62 | static int omapfb_get_recommended_bpp(struct omapfb2_device *fbdev, |
63 | struct omap_dss_device *dssdev); | 63 | struct omap_dss_device *dssdev); |
64 | 64 | ||
65 | #ifdef DEBUG | 65 | #ifdef DEBUG |
66 | static void draw_pixel(struct fb_info *fbi, int x, int y, unsigned color) | 66 | static void draw_pixel(struct fb_info *fbi, int x, int y, unsigned color) |
67 | { | 67 | { |
68 | struct fb_var_screeninfo *var = &fbi->var; | 68 | struct fb_var_screeninfo *var = &fbi->var; |
69 | struct fb_fix_screeninfo *fix = &fbi->fix; | 69 | struct fb_fix_screeninfo *fix = &fbi->fix; |
70 | void __iomem *addr = fbi->screen_base; | 70 | void __iomem *addr = fbi->screen_base; |
71 | const unsigned bytespp = var->bits_per_pixel >> 3; | 71 | const unsigned bytespp = var->bits_per_pixel >> 3; |
72 | const unsigned line_len = fix->line_length / bytespp; | 72 | const unsigned line_len = fix->line_length / bytespp; |
73 | 73 | ||
74 | int r = (color >> 16) & 0xff; | 74 | int r = (color >> 16) & 0xff; |
75 | int g = (color >> 8) & 0xff; | 75 | int g = (color >> 8) & 0xff; |
76 | int b = (color >> 0) & 0xff; | 76 | int b = (color >> 0) & 0xff; |
77 | 77 | ||
78 | if (var->bits_per_pixel == 16) { | 78 | if (var->bits_per_pixel == 16) { |
79 | u16 __iomem *p = (u16 __iomem *)addr; | 79 | u16 __iomem *p = (u16 __iomem *)addr; |
80 | p += y * line_len + x; | 80 | p += y * line_len + x; |
81 | 81 | ||
82 | r = r * 32 / 256; | 82 | r = r * 32 / 256; |
83 | g = g * 64 / 256; | 83 | g = g * 64 / 256; |
84 | b = b * 32 / 256; | 84 | b = b * 32 / 256; |
85 | 85 | ||
86 | __raw_writew((r << 11) | (g << 5) | (b << 0), p); | 86 | __raw_writew((r << 11) | (g << 5) | (b << 0), p); |
87 | } else if (var->bits_per_pixel == 24) { | 87 | } else if (var->bits_per_pixel == 24) { |
88 | u8 __iomem *p = (u8 __iomem *)addr; | 88 | u8 __iomem *p = (u8 __iomem *)addr; |
89 | p += (y * line_len + x) * 3; | 89 | p += (y * line_len + x) * 3; |
90 | 90 | ||
91 | __raw_writeb(b, p + 0); | 91 | __raw_writeb(b, p + 0); |
92 | __raw_writeb(g, p + 1); | 92 | __raw_writeb(g, p + 1); |
93 | __raw_writeb(r, p + 2); | 93 | __raw_writeb(r, p + 2); |
94 | } else if (var->bits_per_pixel == 32) { | 94 | } else if (var->bits_per_pixel == 32) { |
95 | u32 __iomem *p = (u32 __iomem *)addr; | 95 | u32 __iomem *p = (u32 __iomem *)addr; |
96 | p += y * line_len + x; | 96 | p += y * line_len + x; |
97 | __raw_writel(color, p); | 97 | __raw_writel(color, p); |
98 | } | 98 | } |
99 | } | 99 | } |
100 | 100 | ||
101 | static void fill_fb(struct fb_info *fbi) | 101 | static void fill_fb(struct fb_info *fbi) |
102 | { | 102 | { |
103 | struct fb_var_screeninfo *var = &fbi->var; | 103 | struct fb_var_screeninfo *var = &fbi->var; |
104 | const short w = var->xres_virtual; | 104 | const short w = var->xres_virtual; |
105 | const short h = var->yres_virtual; | 105 | const short h = var->yres_virtual; |
106 | void __iomem *addr = fbi->screen_base; | 106 | void __iomem *addr = fbi->screen_base; |
107 | int y, x; | 107 | int y, x; |
108 | 108 | ||
109 | if (!addr) | 109 | if (!addr) |
110 | return; | 110 | return; |
111 | 111 | ||
112 | DBG("fill_fb %dx%d, line_len %d bytes\n", w, h, fbi->fix.line_length); | 112 | DBG("fill_fb %dx%d, line_len %d bytes\n", w, h, fbi->fix.line_length); |
113 | 113 | ||
114 | for (y = 0; y < h; y++) { | 114 | for (y = 0; y < h; y++) { |
115 | for (x = 0; x < w; x++) { | 115 | for (x = 0; x < w; x++) { |
116 | if (x < 20 && y < 20) | 116 | if (x < 20 && y < 20) |
117 | draw_pixel(fbi, x, y, 0xffffff); | 117 | draw_pixel(fbi, x, y, 0xffffff); |
118 | else if (x < 20 && (y > 20 && y < h - 20)) | 118 | else if (x < 20 && (y > 20 && y < h - 20)) |
119 | draw_pixel(fbi, x, y, 0xff); | 119 | draw_pixel(fbi, x, y, 0xff); |
120 | else if (y < 20 && (x > 20 && x < w - 20)) | 120 | else if (y < 20 && (x > 20 && x < w - 20)) |
121 | draw_pixel(fbi, x, y, 0xff00); | 121 | draw_pixel(fbi, x, y, 0xff00); |
122 | else if (x > w - 20 && (y > 20 && y < h - 20)) | 122 | else if (x > w - 20 && (y > 20 && y < h - 20)) |
123 | draw_pixel(fbi, x, y, 0xff0000); | 123 | draw_pixel(fbi, x, y, 0xff0000); |
124 | else if (y > h - 20 && (x > 20 && x < w - 20)) | 124 | else if (y > h - 20 && (x > 20 && x < w - 20)) |
125 | draw_pixel(fbi, x, y, 0xffff00); | 125 | draw_pixel(fbi, x, y, 0xffff00); |
126 | else if (x == 20 || x == w - 20 || | 126 | else if (x == 20 || x == w - 20 || |
127 | y == 20 || y == h - 20) | 127 | y == 20 || y == h - 20) |
128 | draw_pixel(fbi, x, y, 0xffffff); | 128 | draw_pixel(fbi, x, y, 0xffffff); |
129 | else if (x == y || w - x == h - y) | 129 | else if (x == y || w - x == h - y) |
130 | draw_pixel(fbi, x, y, 0xff00ff); | 130 | draw_pixel(fbi, x, y, 0xff00ff); |
131 | else if (w - x == y || x == h - y) | 131 | else if (w - x == y || x == h - y) |
132 | draw_pixel(fbi, x, y, 0x00ffff); | 132 | draw_pixel(fbi, x, y, 0x00ffff); |
133 | else if (x > 20 && y > 20 && x < w - 20 && y < h - 20) { | 133 | else if (x > 20 && y > 20 && x < w - 20 && y < h - 20) { |
134 | int t = x * 3 / w; | 134 | int t = x * 3 / w; |
135 | unsigned r = 0, g = 0, b = 0; | 135 | unsigned r = 0, g = 0, b = 0; |
136 | unsigned c; | 136 | unsigned c; |
137 | if (var->bits_per_pixel == 16) { | 137 | if (var->bits_per_pixel == 16) { |
138 | if (t == 0) | 138 | if (t == 0) |
139 | b = (y % 32) * 256 / 32; | 139 | b = (y % 32) * 256 / 32; |
140 | else if (t == 1) | 140 | else if (t == 1) |
141 | g = (y % 64) * 256 / 64; | 141 | g = (y % 64) * 256 / 64; |
142 | else if (t == 2) | 142 | else if (t == 2) |
143 | r = (y % 32) * 256 / 32; | 143 | r = (y % 32) * 256 / 32; |
144 | } else { | 144 | } else { |
145 | if (t == 0) | 145 | if (t == 0) |
146 | b = (y % 256); | 146 | b = (y % 256); |
147 | else if (t == 1) | 147 | else if (t == 1) |
148 | g = (y % 256); | 148 | g = (y % 256); |
149 | else if (t == 2) | 149 | else if (t == 2) |
150 | r = (y % 256); | 150 | r = (y % 256); |
151 | } | 151 | } |
152 | c = (r << 16) | (g << 8) | (b << 0); | 152 | c = (r << 16) | (g << 8) | (b << 0); |
153 | draw_pixel(fbi, x, y, c); | 153 | draw_pixel(fbi, x, y, c); |
154 | } else { | 154 | } else { |
155 | draw_pixel(fbi, x, y, 0); | 155 | draw_pixel(fbi, x, y, 0); |
156 | } | 156 | } |
157 | } | 157 | } |
158 | } | 158 | } |
159 | } | 159 | } |
160 | #endif | 160 | #endif |
161 | 161 | ||
162 | static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot) | 162 | static unsigned omapfb_get_vrfb_offset(const struct omapfb_info *ofbi, int rot) |
163 | { | 163 | { |
164 | const struct vrfb *vrfb = &ofbi->region->vrfb; | 164 | const struct vrfb *vrfb = &ofbi->region->vrfb; |
165 | unsigned offset; | 165 | unsigned offset; |
166 | 166 | ||
167 | switch (rot) { | 167 | switch (rot) { |
168 | case FB_ROTATE_UR: | 168 | case FB_ROTATE_UR: |
169 | offset = 0; | 169 | offset = 0; |
170 | break; | 170 | break; |
171 | case FB_ROTATE_CW: | 171 | case FB_ROTATE_CW: |
172 | offset = vrfb->yoffset; | 172 | offset = vrfb->yoffset; |
173 | break; | 173 | break; |
174 | case FB_ROTATE_UD: | 174 | case FB_ROTATE_UD: |
175 | offset = vrfb->yoffset * OMAP_VRFB_LINE_LEN + vrfb->xoffset; | 175 | offset = vrfb->yoffset * OMAP_VRFB_LINE_LEN + vrfb->xoffset; |
176 | break; | 176 | break; |
177 | case FB_ROTATE_CCW: | 177 | case FB_ROTATE_CCW: |
178 | offset = vrfb->xoffset * OMAP_VRFB_LINE_LEN; | 178 | offset = vrfb->xoffset * OMAP_VRFB_LINE_LEN; |
179 | break; | 179 | break; |
180 | default: | 180 | default: |
181 | BUG(); | 181 | BUG(); |
182 | return 0; | 182 | return 0; |
183 | } | 183 | } |
184 | 184 | ||
185 | offset *= vrfb->bytespp; | 185 | offset *= vrfb->bytespp; |
186 | 186 | ||
187 | return offset; | 187 | return offset; |
188 | } | 188 | } |
189 | 189 | ||
190 | static u32 omapfb_get_region_rot_paddr(const struct omapfb_info *ofbi, int rot) | 190 | static u32 omapfb_get_region_rot_paddr(const struct omapfb_info *ofbi, int rot) |
191 | { | 191 | { |
192 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { | 192 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { |
193 | return ofbi->region->vrfb.paddr[rot] | 193 | return ofbi->region->vrfb.paddr[rot] |
194 | + omapfb_get_vrfb_offset(ofbi, rot); | 194 | + omapfb_get_vrfb_offset(ofbi, rot); |
195 | } else { | 195 | } else { |
196 | return ofbi->region->paddr; | 196 | return ofbi->region->paddr; |
197 | } | 197 | } |
198 | } | 198 | } |
199 | 199 | ||
200 | static u32 omapfb_get_region_paddr(const struct omapfb_info *ofbi) | 200 | static u32 omapfb_get_region_paddr(const struct omapfb_info *ofbi) |
201 | { | 201 | { |
202 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) | 202 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) |
203 | return ofbi->region->vrfb.paddr[0]; | 203 | return ofbi->region->vrfb.paddr[0]; |
204 | else | 204 | else |
205 | return ofbi->region->paddr; | 205 | return ofbi->region->paddr; |
206 | } | 206 | } |
207 | 207 | ||
208 | static void __iomem *omapfb_get_region_vaddr(const struct omapfb_info *ofbi) | 208 | static void __iomem *omapfb_get_region_vaddr(const struct omapfb_info *ofbi) |
209 | { | 209 | { |
210 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) | 210 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) |
211 | return ofbi->region->vrfb.vaddr[0]; | 211 | return ofbi->region->vrfb.vaddr[0]; |
212 | else | 212 | else |
213 | return ofbi->region->vaddr; | 213 | return ofbi->region->vaddr; |
214 | } | 214 | } |
215 | 215 | ||
216 | static struct omapfb_colormode omapfb_colormodes[] = { | 216 | static struct omapfb_colormode omapfb_colormodes[] = { |
217 | { | 217 | { |
218 | .dssmode = OMAP_DSS_COLOR_UYVY, | 218 | .dssmode = OMAP_DSS_COLOR_UYVY, |
219 | .bits_per_pixel = 16, | 219 | .bits_per_pixel = 16, |
220 | .nonstd = OMAPFB_COLOR_YUV422, | 220 | .nonstd = OMAPFB_COLOR_YUV422, |
221 | }, { | 221 | }, { |
222 | .dssmode = OMAP_DSS_COLOR_YUV2, | 222 | .dssmode = OMAP_DSS_COLOR_YUV2, |
223 | .bits_per_pixel = 16, | 223 | .bits_per_pixel = 16, |
224 | .nonstd = OMAPFB_COLOR_YUY422, | 224 | .nonstd = OMAPFB_COLOR_YUY422, |
225 | }, { | 225 | }, { |
226 | .dssmode = OMAP_DSS_COLOR_ARGB16, | 226 | .dssmode = OMAP_DSS_COLOR_ARGB16, |
227 | .bits_per_pixel = 16, | 227 | .bits_per_pixel = 16, |
228 | .red = { .length = 4, .offset = 8, .msb_right = 0 }, | 228 | .red = { .length = 4, .offset = 8, .msb_right = 0 }, |
229 | .green = { .length = 4, .offset = 4, .msb_right = 0 }, | 229 | .green = { .length = 4, .offset = 4, .msb_right = 0 }, |
230 | .blue = { .length = 4, .offset = 0, .msb_right = 0 }, | 230 | .blue = { .length = 4, .offset = 0, .msb_right = 0 }, |
231 | .transp = { .length = 4, .offset = 12, .msb_right = 0 }, | 231 | .transp = { .length = 4, .offset = 12, .msb_right = 0 }, |
232 | }, { | 232 | }, { |
233 | .dssmode = OMAP_DSS_COLOR_RGB16, | 233 | .dssmode = OMAP_DSS_COLOR_RGB16, |
234 | .bits_per_pixel = 16, | 234 | .bits_per_pixel = 16, |
235 | .red = { .length = 5, .offset = 11, .msb_right = 0 }, | 235 | .red = { .length = 5, .offset = 11, .msb_right = 0 }, |
236 | .green = { .length = 6, .offset = 5, .msb_right = 0 }, | 236 | .green = { .length = 6, .offset = 5, .msb_right = 0 }, |
237 | .blue = { .length = 5, .offset = 0, .msb_right = 0 }, | 237 | .blue = { .length = 5, .offset = 0, .msb_right = 0 }, |
238 | .transp = { .length = 0, .offset = 0, .msb_right = 0 }, | 238 | .transp = { .length = 0, .offset = 0, .msb_right = 0 }, |
239 | }, { | 239 | }, { |
240 | .dssmode = OMAP_DSS_COLOR_RGB24P, | 240 | .dssmode = OMAP_DSS_COLOR_RGB24P, |
241 | .bits_per_pixel = 24, | 241 | .bits_per_pixel = 24, |
242 | .red = { .length = 8, .offset = 16, .msb_right = 0 }, | 242 | .red = { .length = 8, .offset = 16, .msb_right = 0 }, |
243 | .green = { .length = 8, .offset = 8, .msb_right = 0 }, | 243 | .green = { .length = 8, .offset = 8, .msb_right = 0 }, |
244 | .blue = { .length = 8, .offset = 0, .msb_right = 0 }, | 244 | .blue = { .length = 8, .offset = 0, .msb_right = 0 }, |
245 | .transp = { .length = 0, .offset = 0, .msb_right = 0 }, | 245 | .transp = { .length = 0, .offset = 0, .msb_right = 0 }, |
246 | }, { | 246 | }, { |
247 | .dssmode = OMAP_DSS_COLOR_RGB24U, | 247 | .dssmode = OMAP_DSS_COLOR_RGB24U, |
248 | .bits_per_pixel = 32, | 248 | .bits_per_pixel = 32, |
249 | .red = { .length = 8, .offset = 16, .msb_right = 0 }, | 249 | .red = { .length = 8, .offset = 16, .msb_right = 0 }, |
250 | .green = { .length = 8, .offset = 8, .msb_right = 0 }, | 250 | .green = { .length = 8, .offset = 8, .msb_right = 0 }, |
251 | .blue = { .length = 8, .offset = 0, .msb_right = 0 }, | 251 | .blue = { .length = 8, .offset = 0, .msb_right = 0 }, |
252 | .transp = { .length = 0, .offset = 0, .msb_right = 0 }, | 252 | .transp = { .length = 0, .offset = 0, .msb_right = 0 }, |
253 | }, { | 253 | }, { |
254 | .dssmode = OMAP_DSS_COLOR_ARGB32, | 254 | .dssmode = OMAP_DSS_COLOR_ARGB32, |
255 | .bits_per_pixel = 32, | 255 | .bits_per_pixel = 32, |
256 | .red = { .length = 8, .offset = 16, .msb_right = 0 }, | 256 | .red = { .length = 8, .offset = 16, .msb_right = 0 }, |
257 | .green = { .length = 8, .offset = 8, .msb_right = 0 }, | 257 | .green = { .length = 8, .offset = 8, .msb_right = 0 }, |
258 | .blue = { .length = 8, .offset = 0, .msb_right = 0 }, | 258 | .blue = { .length = 8, .offset = 0, .msb_right = 0 }, |
259 | .transp = { .length = 8, .offset = 24, .msb_right = 0 }, | 259 | .transp = { .length = 8, .offset = 24, .msb_right = 0 }, |
260 | }, { | 260 | }, { |
261 | .dssmode = OMAP_DSS_COLOR_RGBA32, | 261 | .dssmode = OMAP_DSS_COLOR_RGBA32, |
262 | .bits_per_pixel = 32, | 262 | .bits_per_pixel = 32, |
263 | .red = { .length = 8, .offset = 24, .msb_right = 0 }, | 263 | .red = { .length = 8, .offset = 24, .msb_right = 0 }, |
264 | .green = { .length = 8, .offset = 16, .msb_right = 0 }, | 264 | .green = { .length = 8, .offset = 16, .msb_right = 0 }, |
265 | .blue = { .length = 8, .offset = 8, .msb_right = 0 }, | 265 | .blue = { .length = 8, .offset = 8, .msb_right = 0 }, |
266 | .transp = { .length = 8, .offset = 0, .msb_right = 0 }, | 266 | .transp = { .length = 8, .offset = 0, .msb_right = 0 }, |
267 | }, { | 267 | }, { |
268 | .dssmode = OMAP_DSS_COLOR_RGBX32, | 268 | .dssmode = OMAP_DSS_COLOR_RGBX32, |
269 | .bits_per_pixel = 32, | 269 | .bits_per_pixel = 32, |
270 | .red = { .length = 8, .offset = 24, .msb_right = 0 }, | 270 | .red = { .length = 8, .offset = 24, .msb_right = 0 }, |
271 | .green = { .length = 8, .offset = 16, .msb_right = 0 }, | 271 | .green = { .length = 8, .offset = 16, .msb_right = 0 }, |
272 | .blue = { .length = 8, .offset = 8, .msb_right = 0 }, | 272 | .blue = { .length = 8, .offset = 8, .msb_right = 0 }, |
273 | .transp = { .length = 0, .offset = 0, .msb_right = 0 }, | 273 | .transp = { .length = 0, .offset = 0, .msb_right = 0 }, |
274 | }, | 274 | }, |
275 | }; | 275 | }; |
276 | 276 | ||
277 | static bool cmp_var_to_colormode(struct fb_var_screeninfo *var, | 277 | static bool cmp_var_to_colormode(struct fb_var_screeninfo *var, |
278 | struct omapfb_colormode *color) | 278 | struct omapfb_colormode *color) |
279 | { | 279 | { |
280 | bool cmp_component(struct fb_bitfield *f1, struct fb_bitfield *f2) | 280 | bool cmp_component(struct fb_bitfield *f1, struct fb_bitfield *f2) |
281 | { | 281 | { |
282 | return f1->length == f2->length && | 282 | return f1->length == f2->length && |
283 | f1->offset == f2->offset && | 283 | f1->offset == f2->offset && |
284 | f1->msb_right == f2->msb_right; | 284 | f1->msb_right == f2->msb_right; |
285 | } | 285 | } |
286 | 286 | ||
287 | if (var->bits_per_pixel == 0 || | 287 | if (var->bits_per_pixel == 0 || |
288 | var->red.length == 0 || | 288 | var->red.length == 0 || |
289 | var->blue.length == 0 || | 289 | var->blue.length == 0 || |
290 | var->green.length == 0) | 290 | var->green.length == 0) |
291 | return 0; | 291 | return 0; |
292 | 292 | ||
293 | return var->bits_per_pixel == color->bits_per_pixel && | 293 | return var->bits_per_pixel == color->bits_per_pixel && |
294 | cmp_component(&var->red, &color->red) && | 294 | cmp_component(&var->red, &color->red) && |
295 | cmp_component(&var->green, &color->green) && | 295 | cmp_component(&var->green, &color->green) && |
296 | cmp_component(&var->blue, &color->blue) && | 296 | cmp_component(&var->blue, &color->blue) && |
297 | cmp_component(&var->transp, &color->transp); | 297 | cmp_component(&var->transp, &color->transp); |
298 | } | 298 | } |
299 | 299 | ||
300 | static void assign_colormode_to_var(struct fb_var_screeninfo *var, | 300 | static void assign_colormode_to_var(struct fb_var_screeninfo *var, |
301 | struct omapfb_colormode *color) | 301 | struct omapfb_colormode *color) |
302 | { | 302 | { |
303 | var->bits_per_pixel = color->bits_per_pixel; | 303 | var->bits_per_pixel = color->bits_per_pixel; |
304 | var->nonstd = color->nonstd; | 304 | var->nonstd = color->nonstd; |
305 | var->red = color->red; | 305 | var->red = color->red; |
306 | var->green = color->green; | 306 | var->green = color->green; |
307 | var->blue = color->blue; | 307 | var->blue = color->blue; |
308 | var->transp = color->transp; | 308 | var->transp = color->transp; |
309 | } | 309 | } |
310 | 310 | ||
311 | static int fb_mode_to_dss_mode(struct fb_var_screeninfo *var, | 311 | static int fb_mode_to_dss_mode(struct fb_var_screeninfo *var, |
312 | enum omap_color_mode *mode) | 312 | enum omap_color_mode *mode) |
313 | { | 313 | { |
314 | enum omap_color_mode dssmode; | 314 | enum omap_color_mode dssmode; |
315 | int i; | 315 | int i; |
316 | 316 | ||
317 | /* first match with nonstd field */ | 317 | /* first match with nonstd field */ |
318 | if (var->nonstd) { | 318 | if (var->nonstd) { |
319 | for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { | 319 | for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { |
320 | struct omapfb_colormode *m = &omapfb_colormodes[i]; | 320 | struct omapfb_colormode *m = &omapfb_colormodes[i]; |
321 | if (var->nonstd == m->nonstd) { | 321 | if (var->nonstd == m->nonstd) { |
322 | assign_colormode_to_var(var, m); | 322 | assign_colormode_to_var(var, m); |
323 | *mode = m->dssmode; | 323 | *mode = m->dssmode; |
324 | return 0; | 324 | return 0; |
325 | } | 325 | } |
326 | } | 326 | } |
327 | 327 | ||
328 | return -EINVAL; | 328 | return -EINVAL; |
329 | } | 329 | } |
330 | 330 | ||
331 | /* then try exact match of bpp and colors */ | 331 | /* then try exact match of bpp and colors */ |
332 | for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { | 332 | for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { |
333 | struct omapfb_colormode *m = &omapfb_colormodes[i]; | 333 | struct omapfb_colormode *m = &omapfb_colormodes[i]; |
334 | if (cmp_var_to_colormode(var, m)) { | 334 | if (cmp_var_to_colormode(var, m)) { |
335 | assign_colormode_to_var(var, m); | 335 | assign_colormode_to_var(var, m); |
336 | *mode = m->dssmode; | 336 | *mode = m->dssmode; |
337 | return 0; | 337 | return 0; |
338 | } | 338 | } |
339 | } | 339 | } |
340 | 340 | ||
341 | /* match with bpp if user has not filled color fields | 341 | /* match with bpp if user has not filled color fields |
342 | * properly */ | 342 | * properly */ |
343 | switch (var->bits_per_pixel) { | 343 | switch (var->bits_per_pixel) { |
344 | case 1: | 344 | case 1: |
345 | dssmode = OMAP_DSS_COLOR_CLUT1; | 345 | dssmode = OMAP_DSS_COLOR_CLUT1; |
346 | break; | 346 | break; |
347 | case 2: | 347 | case 2: |
348 | dssmode = OMAP_DSS_COLOR_CLUT2; | 348 | dssmode = OMAP_DSS_COLOR_CLUT2; |
349 | break; | 349 | break; |
350 | case 4: | 350 | case 4: |
351 | dssmode = OMAP_DSS_COLOR_CLUT4; | 351 | dssmode = OMAP_DSS_COLOR_CLUT4; |
352 | break; | 352 | break; |
353 | case 8: | 353 | case 8: |
354 | dssmode = OMAP_DSS_COLOR_CLUT8; | 354 | dssmode = OMAP_DSS_COLOR_CLUT8; |
355 | break; | 355 | break; |
356 | case 12: | 356 | case 12: |
357 | dssmode = OMAP_DSS_COLOR_RGB12U; | 357 | dssmode = OMAP_DSS_COLOR_RGB12U; |
358 | break; | 358 | break; |
359 | case 16: | 359 | case 16: |
360 | dssmode = OMAP_DSS_COLOR_RGB16; | 360 | dssmode = OMAP_DSS_COLOR_RGB16; |
361 | break; | 361 | break; |
362 | case 24: | 362 | case 24: |
363 | dssmode = OMAP_DSS_COLOR_RGB24P; | 363 | dssmode = OMAP_DSS_COLOR_RGB24P; |
364 | break; | 364 | break; |
365 | case 32: | 365 | case 32: |
366 | dssmode = OMAP_DSS_COLOR_RGB24U; | 366 | dssmode = OMAP_DSS_COLOR_RGB24U; |
367 | break; | 367 | break; |
368 | default: | 368 | default: |
369 | return -EINVAL; | 369 | return -EINVAL; |
370 | } | 370 | } |
371 | 371 | ||
372 | for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { | 372 | for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { |
373 | struct omapfb_colormode *m = &omapfb_colormodes[i]; | 373 | struct omapfb_colormode *m = &omapfb_colormodes[i]; |
374 | if (dssmode == m->dssmode) { | 374 | if (dssmode == m->dssmode) { |
375 | assign_colormode_to_var(var, m); | 375 | assign_colormode_to_var(var, m); |
376 | *mode = m->dssmode; | 376 | *mode = m->dssmode; |
377 | return 0; | 377 | return 0; |
378 | } | 378 | } |
379 | } | 379 | } |
380 | 380 | ||
381 | return -EINVAL; | 381 | return -EINVAL; |
382 | } | 382 | } |
383 | 383 | ||
384 | static int check_fb_res_bounds(struct fb_var_screeninfo *var) | 384 | static int check_fb_res_bounds(struct fb_var_screeninfo *var) |
385 | { | 385 | { |
386 | int xres_min = OMAPFB_PLANE_XRES_MIN; | 386 | int xres_min = OMAPFB_PLANE_XRES_MIN; |
387 | int xres_max = 2048; | 387 | int xres_max = 2048; |
388 | int yres_min = OMAPFB_PLANE_YRES_MIN; | 388 | int yres_min = OMAPFB_PLANE_YRES_MIN; |
389 | int yres_max = 2048; | 389 | int yres_max = 2048; |
390 | 390 | ||
391 | /* XXX: some applications seem to set virtual res to 0. */ | 391 | /* XXX: some applications seem to set virtual res to 0. */ |
392 | if (var->xres_virtual == 0) | 392 | if (var->xres_virtual == 0) |
393 | var->xres_virtual = var->xres; | 393 | var->xres_virtual = var->xres; |
394 | 394 | ||
395 | if (var->yres_virtual == 0) | 395 | if (var->yres_virtual == 0) |
396 | var->yres_virtual = var->yres; | 396 | var->yres_virtual = var->yres; |
397 | 397 | ||
398 | if (var->xres_virtual < xres_min || var->yres_virtual < yres_min) | 398 | if (var->xres_virtual < xres_min || var->yres_virtual < yres_min) |
399 | return -EINVAL; | 399 | return -EINVAL; |
400 | 400 | ||
401 | if (var->xres < xres_min) | 401 | if (var->xres < xres_min) |
402 | var->xres = xres_min; | 402 | var->xres = xres_min; |
403 | if (var->yres < yres_min) | 403 | if (var->yres < yres_min) |
404 | var->yres = yres_min; | 404 | var->yres = yres_min; |
405 | if (var->xres > xres_max) | 405 | if (var->xres > xres_max) |
406 | var->xres = xres_max; | 406 | var->xres = xres_max; |
407 | if (var->yres > yres_max) | 407 | if (var->yres > yres_max) |
408 | var->yres = yres_max; | 408 | var->yres = yres_max; |
409 | 409 | ||
410 | if (var->xres > var->xres_virtual) | 410 | if (var->xres > var->xres_virtual) |
411 | var->xres = var->xres_virtual; | 411 | var->xres = var->xres_virtual; |
412 | if (var->yres > var->yres_virtual) | 412 | if (var->yres > var->yres_virtual) |
413 | var->yres = var->yres_virtual; | 413 | var->yres = var->yres_virtual; |
414 | 414 | ||
415 | return 0; | 415 | return 0; |
416 | } | 416 | } |
417 | 417 | ||
418 | static void shrink_height(unsigned long max_frame_size, | 418 | static void shrink_height(unsigned long max_frame_size, |
419 | struct fb_var_screeninfo *var) | 419 | struct fb_var_screeninfo *var) |
420 | { | 420 | { |
421 | DBG("can't fit FB into memory, reducing y\n"); | 421 | DBG("can't fit FB into memory, reducing y\n"); |
422 | var->yres_virtual = max_frame_size / | 422 | var->yres_virtual = max_frame_size / |
423 | (var->xres_virtual * var->bits_per_pixel >> 3); | 423 | (var->xres_virtual * var->bits_per_pixel >> 3); |
424 | 424 | ||
425 | if (var->yres_virtual < OMAPFB_PLANE_YRES_MIN) | 425 | if (var->yres_virtual < OMAPFB_PLANE_YRES_MIN) |
426 | var->yres_virtual = OMAPFB_PLANE_YRES_MIN; | 426 | var->yres_virtual = OMAPFB_PLANE_YRES_MIN; |
427 | 427 | ||
428 | if (var->yres > var->yres_virtual) | 428 | if (var->yres > var->yres_virtual) |
429 | var->yres = var->yres_virtual; | 429 | var->yres = var->yres_virtual; |
430 | } | 430 | } |
431 | 431 | ||
432 | static void shrink_width(unsigned long max_frame_size, | 432 | static void shrink_width(unsigned long max_frame_size, |
433 | struct fb_var_screeninfo *var) | 433 | struct fb_var_screeninfo *var) |
434 | { | 434 | { |
435 | DBG("can't fit FB into memory, reducing x\n"); | 435 | DBG("can't fit FB into memory, reducing x\n"); |
436 | var->xres_virtual = max_frame_size / var->yres_virtual / | 436 | var->xres_virtual = max_frame_size / var->yres_virtual / |
437 | (var->bits_per_pixel >> 3); | 437 | (var->bits_per_pixel >> 3); |
438 | 438 | ||
439 | if (var->xres_virtual < OMAPFB_PLANE_XRES_MIN) | 439 | if (var->xres_virtual < OMAPFB_PLANE_XRES_MIN) |
440 | var->xres_virtual = OMAPFB_PLANE_XRES_MIN; | 440 | var->xres_virtual = OMAPFB_PLANE_XRES_MIN; |
441 | 441 | ||
442 | if (var->xres > var->xres_virtual) | 442 | if (var->xres > var->xres_virtual) |
443 | var->xres = var->xres_virtual; | 443 | var->xres = var->xres_virtual; |
444 | } | 444 | } |
445 | 445 | ||
446 | static int check_vrfb_fb_size(unsigned long region_size, | 446 | static int check_vrfb_fb_size(unsigned long region_size, |
447 | const struct fb_var_screeninfo *var) | 447 | const struct fb_var_screeninfo *var) |
448 | { | 448 | { |
449 | unsigned long min_phys_size = omap_vrfb_min_phys_size(var->xres_virtual, | 449 | unsigned long min_phys_size = omap_vrfb_min_phys_size(var->xres_virtual, |
450 | var->yres_virtual, var->bits_per_pixel >> 3); | 450 | var->yres_virtual, var->bits_per_pixel >> 3); |
451 | 451 | ||
452 | return min_phys_size > region_size ? -EINVAL : 0; | 452 | return min_phys_size > region_size ? -EINVAL : 0; |
453 | } | 453 | } |
454 | 454 | ||
455 | static int check_fb_size(const struct omapfb_info *ofbi, | 455 | static int check_fb_size(const struct omapfb_info *ofbi, |
456 | struct fb_var_screeninfo *var) | 456 | struct fb_var_screeninfo *var) |
457 | { | 457 | { |
458 | unsigned long max_frame_size = ofbi->region->size; | 458 | unsigned long max_frame_size = ofbi->region->size; |
459 | int bytespp = var->bits_per_pixel >> 3; | 459 | int bytespp = var->bits_per_pixel >> 3; |
460 | unsigned long line_size = var->xres_virtual * bytespp; | 460 | unsigned long line_size = var->xres_virtual * bytespp; |
461 | 461 | ||
462 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { | 462 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { |
463 | /* One needs to check for both VRFB and OMAPFB limitations. */ | 463 | /* One needs to check for both VRFB and OMAPFB limitations. */ |
464 | if (check_vrfb_fb_size(max_frame_size, var)) | 464 | if (check_vrfb_fb_size(max_frame_size, var)) |
465 | shrink_height(omap_vrfb_max_height( | 465 | shrink_height(omap_vrfb_max_height( |
466 | max_frame_size, var->xres_virtual, bytespp) * | 466 | max_frame_size, var->xres_virtual, bytespp) * |
467 | line_size, var); | 467 | line_size, var); |
468 | 468 | ||
469 | if (check_vrfb_fb_size(max_frame_size, var)) { | 469 | if (check_vrfb_fb_size(max_frame_size, var)) { |
470 | DBG("cannot fit FB to memory\n"); | 470 | DBG("cannot fit FB to memory\n"); |
471 | return -EINVAL; | 471 | return -EINVAL; |
472 | } | 472 | } |
473 | 473 | ||
474 | return 0; | 474 | return 0; |
475 | } | 475 | } |
476 | 476 | ||
477 | DBG("max frame size %lu, line size %lu\n", max_frame_size, line_size); | 477 | DBG("max frame size %lu, line size %lu\n", max_frame_size, line_size); |
478 | 478 | ||
479 | if (line_size * var->yres_virtual > max_frame_size) | 479 | if (line_size * var->yres_virtual > max_frame_size) |
480 | shrink_height(max_frame_size, var); | 480 | shrink_height(max_frame_size, var); |
481 | 481 | ||
482 | if (line_size * var->yres_virtual > max_frame_size) { | 482 | if (line_size * var->yres_virtual > max_frame_size) { |
483 | shrink_width(max_frame_size, var); | 483 | shrink_width(max_frame_size, var); |
484 | line_size = var->xres_virtual * bytespp; | 484 | line_size = var->xres_virtual * bytespp; |
485 | } | 485 | } |
486 | 486 | ||
487 | if (line_size * var->yres_virtual > max_frame_size) { | 487 | if (line_size * var->yres_virtual > max_frame_size) { |
488 | DBG("cannot fit FB to memory\n"); | 488 | DBG("cannot fit FB to memory\n"); |
489 | return -EINVAL; | 489 | return -EINVAL; |
490 | } | 490 | } |
491 | 491 | ||
492 | return 0; | 492 | return 0; |
493 | } | 493 | } |
494 | 494 | ||
495 | /* | 495 | /* |
496 | * Consider if VRFB assisted rotation is in use and if the virtual space for | 496 | * Consider if VRFB assisted rotation is in use and if the virtual space for |
497 | * the zero degree view needs to be mapped. The need for mapping also acts as | 497 | * the zero degree view needs to be mapped. The need for mapping also acts as |
498 | * the trigger for setting up the hardware on the context in question. This | 498 | * the trigger for setting up the hardware on the context in question. This |
499 | * ensures that one does not attempt to access the virtual view before the | 499 | * ensures that one does not attempt to access the virtual view before the |
500 | * hardware is serving the address translations. | 500 | * hardware is serving the address translations. |
501 | */ | 501 | */ |
502 | static int setup_vrfb_rotation(struct fb_info *fbi) | 502 | static int setup_vrfb_rotation(struct fb_info *fbi) |
503 | { | 503 | { |
504 | struct omapfb_info *ofbi = FB2OFB(fbi); | 504 | struct omapfb_info *ofbi = FB2OFB(fbi); |
505 | struct omapfb2_mem_region *rg = ofbi->region; | 505 | struct omapfb2_mem_region *rg = ofbi->region; |
506 | struct vrfb *vrfb = &rg->vrfb; | 506 | struct vrfb *vrfb = &rg->vrfb; |
507 | struct fb_var_screeninfo *var = &fbi->var; | 507 | struct fb_var_screeninfo *var = &fbi->var; |
508 | struct fb_fix_screeninfo *fix = &fbi->fix; | 508 | struct fb_fix_screeninfo *fix = &fbi->fix; |
509 | unsigned bytespp; | 509 | unsigned bytespp; |
510 | bool yuv_mode; | 510 | bool yuv_mode; |
511 | enum omap_color_mode mode; | 511 | enum omap_color_mode mode; |
512 | int r; | 512 | int r; |
513 | bool reconf; | 513 | bool reconf; |
514 | 514 | ||
515 | if (!rg->size || ofbi->rotation_type != OMAP_DSS_ROT_VRFB) | 515 | if (!rg->size || ofbi->rotation_type != OMAP_DSS_ROT_VRFB) |
516 | return 0; | 516 | return 0; |
517 | 517 | ||
518 | DBG("setup_vrfb_rotation\n"); | 518 | DBG("setup_vrfb_rotation\n"); |
519 | 519 | ||
520 | r = fb_mode_to_dss_mode(var, &mode); | 520 | r = fb_mode_to_dss_mode(var, &mode); |
521 | if (r) | 521 | if (r) |
522 | return r; | 522 | return r; |
523 | 523 | ||
524 | bytespp = var->bits_per_pixel >> 3; | 524 | bytespp = var->bits_per_pixel >> 3; |
525 | 525 | ||
526 | yuv_mode = mode == OMAP_DSS_COLOR_YUV2 || mode == OMAP_DSS_COLOR_UYVY; | 526 | yuv_mode = mode == OMAP_DSS_COLOR_YUV2 || mode == OMAP_DSS_COLOR_UYVY; |
527 | 527 | ||
528 | /* We need to reconfigure VRFB if the resolution changes, if yuv mode | 528 | /* We need to reconfigure VRFB if the resolution changes, if yuv mode |
529 | * is enabled/disabled, or if bytes per pixel changes */ | 529 | * is enabled/disabled, or if bytes per pixel changes */ |
530 | 530 | ||
531 | /* XXX we shouldn't allow this when framebuffer is mmapped */ | 531 | /* XXX we shouldn't allow this when framebuffer is mmapped */ |
532 | 532 | ||
533 | reconf = false; | 533 | reconf = false; |
534 | 534 | ||
535 | if (yuv_mode != vrfb->yuv_mode) | 535 | if (yuv_mode != vrfb->yuv_mode) |
536 | reconf = true; | 536 | reconf = true; |
537 | else if (bytespp != vrfb->bytespp) | 537 | else if (bytespp != vrfb->bytespp) |
538 | reconf = true; | 538 | reconf = true; |
539 | else if (vrfb->xres != var->xres_virtual || | 539 | else if (vrfb->xres != var->xres_virtual || |
540 | vrfb->yres != var->yres_virtual) | 540 | vrfb->yres != var->yres_virtual) |
541 | reconf = true; | 541 | reconf = true; |
542 | 542 | ||
543 | if (vrfb->vaddr[0] && reconf) { | 543 | if (vrfb->vaddr[0] && reconf) { |
544 | fbi->screen_base = NULL; | 544 | fbi->screen_base = NULL; |
545 | fix->smem_start = 0; | 545 | fix->smem_start = 0; |
546 | fix->smem_len = 0; | 546 | fix->smem_len = 0; |
547 | iounmap(vrfb->vaddr[0]); | 547 | iounmap(vrfb->vaddr[0]); |
548 | vrfb->vaddr[0] = NULL; | 548 | vrfb->vaddr[0] = NULL; |
549 | DBG("setup_vrfb_rotation: reset fb\n"); | 549 | DBG("setup_vrfb_rotation: reset fb\n"); |
550 | } | 550 | } |
551 | 551 | ||
552 | if (vrfb->vaddr[0]) | 552 | if (vrfb->vaddr[0]) |
553 | return 0; | 553 | return 0; |
554 | 554 | ||
555 | omap_vrfb_setup(&rg->vrfb, rg->paddr, | 555 | omap_vrfb_setup(&rg->vrfb, rg->paddr, |
556 | var->xres_virtual, | 556 | var->xres_virtual, |
557 | var->yres_virtual, | 557 | var->yres_virtual, |
558 | bytespp, yuv_mode); | 558 | bytespp, yuv_mode); |
559 | 559 | ||
560 | /* Now one can ioremap the 0 angle view */ | 560 | /* Now one can ioremap the 0 angle view */ |
561 | r = omap_vrfb_map_angle(vrfb, var->yres_virtual, 0); | 561 | r = omap_vrfb_map_angle(vrfb, var->yres_virtual, 0); |
562 | if (r) | 562 | if (r) |
563 | return r; | 563 | return r; |
564 | 564 | ||
565 | /* used by open/write in fbmem.c */ | 565 | /* used by open/write in fbmem.c */ |
566 | fbi->screen_base = ofbi->region->vrfb.vaddr[0]; | 566 | fbi->screen_base = ofbi->region->vrfb.vaddr[0]; |
567 | 567 | ||
568 | fix->smem_start = ofbi->region->vrfb.paddr[0]; | 568 | fix->smem_start = ofbi->region->vrfb.paddr[0]; |
569 | 569 | ||
570 | switch (var->nonstd) { | 570 | switch (var->nonstd) { |
571 | case OMAPFB_COLOR_YUV422: | 571 | case OMAPFB_COLOR_YUV422: |
572 | case OMAPFB_COLOR_YUY422: | 572 | case OMAPFB_COLOR_YUY422: |
573 | fix->line_length = | 573 | fix->line_length = |
574 | (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2; | 574 | (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2; |
575 | break; | 575 | break; |
576 | default: | 576 | default: |
577 | fix->line_length = | 577 | fix->line_length = |
578 | (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3; | 578 | (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3; |
579 | break; | 579 | break; |
580 | } | 580 | } |
581 | 581 | ||
582 | fix->smem_len = var->yres_virtual * fix->line_length; | 582 | fix->smem_len = var->yres_virtual * fix->line_length; |
583 | 583 | ||
584 | return 0; | 584 | return 0; |
585 | } | 585 | } |
586 | 586 | ||
587 | int dss_mode_to_fb_mode(enum omap_color_mode dssmode, | 587 | int dss_mode_to_fb_mode(enum omap_color_mode dssmode, |
588 | struct fb_var_screeninfo *var) | 588 | struct fb_var_screeninfo *var) |
589 | { | 589 | { |
590 | int i; | 590 | int i; |
591 | 591 | ||
592 | for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { | 592 | for (i = 0; i < ARRAY_SIZE(omapfb_colormodes); ++i) { |
593 | struct omapfb_colormode *mode = &omapfb_colormodes[i]; | 593 | struct omapfb_colormode *mode = &omapfb_colormodes[i]; |
594 | if (dssmode == mode->dssmode) { | 594 | if (dssmode == mode->dssmode) { |
595 | assign_colormode_to_var(var, mode); | 595 | assign_colormode_to_var(var, mode); |
596 | return 0; | 596 | return 0; |
597 | } | 597 | } |
598 | } | 598 | } |
599 | return -ENOENT; | 599 | return -ENOENT; |
600 | } | 600 | } |
601 | 601 | ||
602 | void set_fb_fix(struct fb_info *fbi) | 602 | void set_fb_fix(struct fb_info *fbi) |
603 | { | 603 | { |
604 | struct fb_fix_screeninfo *fix = &fbi->fix; | 604 | struct fb_fix_screeninfo *fix = &fbi->fix; |
605 | struct fb_var_screeninfo *var = &fbi->var; | 605 | struct fb_var_screeninfo *var = &fbi->var; |
606 | struct omapfb_info *ofbi = FB2OFB(fbi); | 606 | struct omapfb_info *ofbi = FB2OFB(fbi); |
607 | struct omapfb2_mem_region *rg = ofbi->region; | 607 | struct omapfb2_mem_region *rg = ofbi->region; |
608 | 608 | ||
609 | DBG("set_fb_fix\n"); | 609 | DBG("set_fb_fix\n"); |
610 | 610 | ||
611 | /* used by open/write in fbmem.c */ | 611 | /* used by open/write in fbmem.c */ |
612 | fbi->screen_base = (char __iomem *)omapfb_get_region_vaddr(ofbi); | 612 | fbi->screen_base = (char __iomem *)omapfb_get_region_vaddr(ofbi); |
613 | 613 | ||
614 | /* used by mmap in fbmem.c */ | 614 | /* used by mmap in fbmem.c */ |
615 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { | 615 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { |
616 | switch (var->nonstd) { | 616 | switch (var->nonstd) { |
617 | case OMAPFB_COLOR_YUV422: | 617 | case OMAPFB_COLOR_YUV422: |
618 | case OMAPFB_COLOR_YUY422: | 618 | case OMAPFB_COLOR_YUY422: |
619 | fix->line_length = | 619 | fix->line_length = |
620 | (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2; | 620 | (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 2; |
621 | break; | 621 | break; |
622 | default: | 622 | default: |
623 | fix->line_length = | 623 | fix->line_length = |
624 | (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3; | 624 | (OMAP_VRFB_LINE_LEN * var->bits_per_pixel) >> 3; |
625 | break; | 625 | break; |
626 | } | 626 | } |
627 | 627 | ||
628 | fix->smem_len = var->yres_virtual * fix->line_length; | 628 | fix->smem_len = var->yres_virtual * fix->line_length; |
629 | } else { | 629 | } else { |
630 | fix->line_length = | 630 | fix->line_length = |
631 | (var->xres_virtual * var->bits_per_pixel) >> 3; | 631 | (var->xres_virtual * var->bits_per_pixel) >> 3; |
632 | fix->smem_len = rg->size; | 632 | fix->smem_len = rg->size; |
633 | } | 633 | } |
634 | 634 | ||
635 | fix->smem_start = omapfb_get_region_paddr(ofbi); | 635 | fix->smem_start = omapfb_get_region_paddr(ofbi); |
636 | 636 | ||
637 | fix->type = FB_TYPE_PACKED_PIXELS; | 637 | fix->type = FB_TYPE_PACKED_PIXELS; |
638 | 638 | ||
639 | if (var->nonstd) | 639 | if (var->nonstd) |
640 | fix->visual = FB_VISUAL_PSEUDOCOLOR; | 640 | fix->visual = FB_VISUAL_PSEUDOCOLOR; |
641 | else { | 641 | else { |
642 | switch (var->bits_per_pixel) { | 642 | switch (var->bits_per_pixel) { |
643 | case 32: | 643 | case 32: |
644 | case 24: | 644 | case 24: |
645 | case 16: | 645 | case 16: |
646 | case 12: | 646 | case 12: |
647 | fix->visual = FB_VISUAL_TRUECOLOR; | 647 | fix->visual = FB_VISUAL_TRUECOLOR; |
648 | /* 12bpp is stored in 16 bits */ | 648 | /* 12bpp is stored in 16 bits */ |
649 | break; | 649 | break; |
650 | case 1: | 650 | case 1: |
651 | case 2: | 651 | case 2: |
652 | case 4: | 652 | case 4: |
653 | case 8: | 653 | case 8: |
654 | fix->visual = FB_VISUAL_PSEUDOCOLOR; | 654 | fix->visual = FB_VISUAL_PSEUDOCOLOR; |
655 | break; | 655 | break; |
656 | } | 656 | } |
657 | } | 657 | } |
658 | 658 | ||
659 | fix->accel = FB_ACCEL_NONE; | 659 | fix->accel = FB_ACCEL_NONE; |
660 | 660 | ||
661 | fix->xpanstep = 1; | 661 | fix->xpanstep = 1; |
662 | fix->ypanstep = 1; | 662 | fix->ypanstep = 1; |
663 | } | 663 | } |
664 | 664 | ||
665 | /* check new var and possibly modify it to be ok */ | 665 | /* check new var and possibly modify it to be ok */ |
666 | int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var) | 666 | int check_fb_var(struct fb_info *fbi, struct fb_var_screeninfo *var) |
667 | { | 667 | { |
668 | struct omapfb_info *ofbi = FB2OFB(fbi); | 668 | struct omapfb_info *ofbi = FB2OFB(fbi); |
669 | struct omap_dss_device *display = fb2display(fbi); | 669 | struct omap_dss_device *display = fb2display(fbi); |
670 | enum omap_color_mode mode = 0; | 670 | enum omap_color_mode mode = 0; |
671 | int i; | 671 | int i; |
672 | int r; | 672 | int r; |
673 | 673 | ||
674 | DBG("check_fb_var %d\n", ofbi->id); | 674 | DBG("check_fb_var %d\n", ofbi->id); |
675 | 675 | ||
676 | WARN_ON(!atomic_read(&ofbi->region->lock_count)); | 676 | WARN_ON(!atomic_read(&ofbi->region->lock_count)); |
677 | 677 | ||
678 | r = fb_mode_to_dss_mode(var, &mode); | 678 | r = fb_mode_to_dss_mode(var, &mode); |
679 | if (r) { | 679 | if (r) { |
680 | DBG("cannot convert var to omap dss mode\n"); | 680 | DBG("cannot convert var to omap dss mode\n"); |
681 | return r; | 681 | return r; |
682 | } | 682 | } |
683 | 683 | ||
684 | for (i = 0; i < ofbi->num_overlays; ++i) { | 684 | for (i = 0; i < ofbi->num_overlays; ++i) { |
685 | if ((ofbi->overlays[i]->supported_modes & mode) == 0) { | 685 | if ((ofbi->overlays[i]->supported_modes & mode) == 0) { |
686 | DBG("invalid mode\n"); | 686 | DBG("invalid mode\n"); |
687 | return -EINVAL; | 687 | return -EINVAL; |
688 | } | 688 | } |
689 | } | 689 | } |
690 | 690 | ||
691 | if (var->rotate > 3) | 691 | if (var->rotate > 3) |
692 | return -EINVAL; | 692 | return -EINVAL; |
693 | 693 | ||
694 | if (check_fb_res_bounds(var)) | 694 | if (check_fb_res_bounds(var)) |
695 | return -EINVAL; | 695 | return -EINVAL; |
696 | 696 | ||
697 | /* When no memory is allocated ignore the size check */ | 697 | /* When no memory is allocated ignore the size check */ |
698 | if (ofbi->region->size != 0 && check_fb_size(ofbi, var)) | 698 | if (ofbi->region->size != 0 && check_fb_size(ofbi, var)) |
699 | return -EINVAL; | 699 | return -EINVAL; |
700 | 700 | ||
701 | if (var->xres + var->xoffset > var->xres_virtual) | 701 | if (var->xres + var->xoffset > var->xres_virtual) |
702 | var->xoffset = var->xres_virtual - var->xres; | 702 | var->xoffset = var->xres_virtual - var->xres; |
703 | if (var->yres + var->yoffset > var->yres_virtual) | 703 | if (var->yres + var->yoffset > var->yres_virtual) |
704 | var->yoffset = var->yres_virtual - var->yres; | 704 | var->yoffset = var->yres_virtual - var->yres; |
705 | 705 | ||
706 | DBG("xres = %d, yres = %d, vxres = %d, vyres = %d\n", | 706 | DBG("xres = %d, yres = %d, vxres = %d, vyres = %d\n", |
707 | var->xres, var->yres, | 707 | var->xres, var->yres, |
708 | var->xres_virtual, var->yres_virtual); | 708 | var->xres_virtual, var->yres_virtual); |
709 | 709 | ||
710 | if (display && display->driver->get_dimensions) { | 710 | if (display && display->driver->get_dimensions) { |
711 | u32 w, h; | 711 | u32 w, h; |
712 | display->driver->get_dimensions(display, &w, &h); | 712 | display->driver->get_dimensions(display, &w, &h); |
713 | var->width = DIV_ROUND_CLOSEST(w, 1000); | 713 | var->width = DIV_ROUND_CLOSEST(w, 1000); |
714 | var->height = DIV_ROUND_CLOSEST(h, 1000); | 714 | var->height = DIV_ROUND_CLOSEST(h, 1000); |
715 | } else { | 715 | } else { |
716 | var->height = -1; | 716 | var->height = -1; |
717 | var->width = -1; | 717 | var->width = -1; |
718 | } | 718 | } |
719 | 719 | ||
720 | var->grayscale = 0; | 720 | var->grayscale = 0; |
721 | 721 | ||
722 | if (display && display->driver->get_timings) { | 722 | if (display && display->driver->get_timings) { |
723 | struct omap_video_timings timings; | 723 | struct omap_video_timings timings; |
724 | display->driver->get_timings(display, &timings); | 724 | display->driver->get_timings(display, &timings); |
725 | 725 | ||
726 | /* pixclock in ps, the rest in pixclock */ | 726 | /* pixclock in ps, the rest in pixclock */ |
727 | var->pixclock = timings.pixel_clock != 0 ? | 727 | var->pixclock = timings.pixel_clock != 0 ? |
728 | KHZ2PICOS(timings.pixel_clock) : | 728 | KHZ2PICOS(timings.pixel_clock) : |
729 | 0; | 729 | 0; |
730 | var->left_margin = timings.hbp; | 730 | var->left_margin = timings.hbp; |
731 | var->right_margin = timings.hfp; | 731 | var->right_margin = timings.hfp; |
732 | var->upper_margin = timings.vbp; | 732 | var->upper_margin = timings.vbp; |
733 | var->lower_margin = timings.vfp; | 733 | var->lower_margin = timings.vfp; |
734 | var->hsync_len = timings.hsw; | 734 | var->hsync_len = timings.hsw; |
735 | var->vsync_len = timings.vsw; | 735 | var->vsync_len = timings.vsw; |
736 | var->sync |= timings.hsync_level == OMAPDSS_SIG_ACTIVE_HIGH ? | 736 | var->sync |= timings.hsync_level == OMAPDSS_SIG_ACTIVE_HIGH ? |
737 | FB_SYNC_HOR_HIGH_ACT : 0; | 737 | FB_SYNC_HOR_HIGH_ACT : 0; |
738 | var->sync |= timings.vsync_level == OMAPDSS_SIG_ACTIVE_HIGH ? | 738 | var->sync |= timings.vsync_level == OMAPDSS_SIG_ACTIVE_HIGH ? |
739 | FB_SYNC_VERT_HIGH_ACT : 0; | 739 | FB_SYNC_VERT_HIGH_ACT : 0; |
740 | var->vmode = timings.interlace ? | 740 | var->vmode = timings.interlace ? |
741 | FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED; | 741 | FB_VMODE_INTERLACED : FB_VMODE_NONINTERLACED; |
742 | } else { | 742 | } else { |
743 | var->pixclock = 0; | 743 | var->pixclock = 0; |
744 | var->left_margin = 0; | 744 | var->left_margin = 0; |
745 | var->right_margin = 0; | 745 | var->right_margin = 0; |
746 | var->upper_margin = 0; | 746 | var->upper_margin = 0; |
747 | var->lower_margin = 0; | 747 | var->lower_margin = 0; |
748 | var->hsync_len = 0; | 748 | var->hsync_len = 0; |
749 | var->vsync_len = 0; | 749 | var->vsync_len = 0; |
750 | var->sync = 0; | 750 | var->sync = 0; |
751 | var->vmode = FB_VMODE_NONINTERLACED; | 751 | var->vmode = FB_VMODE_NONINTERLACED; |
752 | } | 752 | } |
753 | 753 | ||
754 | return 0; | 754 | return 0; |
755 | } | 755 | } |
756 | 756 | ||
757 | /* | 757 | /* |
758 | * --------------------------------------------------------------------------- | 758 | * --------------------------------------------------------------------------- |
759 | * fbdev framework callbacks | 759 | * fbdev framework callbacks |
760 | * --------------------------------------------------------------------------- | 760 | * --------------------------------------------------------------------------- |
761 | */ | 761 | */ |
762 | static int omapfb_open(struct fb_info *fbi, int user) | 762 | static int omapfb_open(struct fb_info *fbi, int user) |
763 | { | 763 | { |
764 | return 0; | 764 | return 0; |
765 | } | 765 | } |
766 | 766 | ||
767 | static int omapfb_release(struct fb_info *fbi, int user) | 767 | static int omapfb_release(struct fb_info *fbi, int user) |
768 | { | 768 | { |
769 | return 0; | 769 | return 0; |
770 | } | 770 | } |
771 | 771 | ||
772 | static unsigned calc_rotation_offset_dma(const struct fb_var_screeninfo *var, | 772 | static unsigned calc_rotation_offset_dma(const struct fb_var_screeninfo *var, |
773 | const struct fb_fix_screeninfo *fix, int rotation) | 773 | const struct fb_fix_screeninfo *fix, int rotation) |
774 | { | 774 | { |
775 | unsigned offset; | 775 | unsigned offset; |
776 | 776 | ||
777 | offset = var->yoffset * fix->line_length + | 777 | offset = var->yoffset * fix->line_length + |
778 | var->xoffset * (var->bits_per_pixel >> 3); | 778 | var->xoffset * (var->bits_per_pixel >> 3); |
779 | 779 | ||
780 | return offset; | 780 | return offset; |
781 | } | 781 | } |
782 | 782 | ||
783 | static unsigned calc_rotation_offset_vrfb(const struct fb_var_screeninfo *var, | 783 | static unsigned calc_rotation_offset_vrfb(const struct fb_var_screeninfo *var, |
784 | const struct fb_fix_screeninfo *fix, int rotation) | 784 | const struct fb_fix_screeninfo *fix, int rotation) |
785 | { | 785 | { |
786 | unsigned offset; | 786 | unsigned offset; |
787 | 787 | ||
788 | if (rotation == FB_ROTATE_UD) | 788 | if (rotation == FB_ROTATE_UD) |
789 | offset = (var->yres_virtual - var->yres) * | 789 | offset = (var->yres_virtual - var->yres) * |
790 | fix->line_length; | 790 | fix->line_length; |
791 | else if (rotation == FB_ROTATE_CW) | 791 | else if (rotation == FB_ROTATE_CW) |
792 | offset = (var->yres_virtual - var->yres) * | 792 | offset = (var->yres_virtual - var->yres) * |
793 | (var->bits_per_pixel >> 3); | 793 | (var->bits_per_pixel >> 3); |
794 | else | 794 | else |
795 | offset = 0; | 795 | offset = 0; |
796 | 796 | ||
797 | if (rotation == FB_ROTATE_UR) | 797 | if (rotation == FB_ROTATE_UR) |
798 | offset += var->yoffset * fix->line_length + | 798 | offset += var->yoffset * fix->line_length + |
799 | var->xoffset * (var->bits_per_pixel >> 3); | 799 | var->xoffset * (var->bits_per_pixel >> 3); |
800 | else if (rotation == FB_ROTATE_UD) | 800 | else if (rotation == FB_ROTATE_UD) |
801 | offset -= var->yoffset * fix->line_length + | 801 | offset -= var->yoffset * fix->line_length + |
802 | var->xoffset * (var->bits_per_pixel >> 3); | 802 | var->xoffset * (var->bits_per_pixel >> 3); |
803 | else if (rotation == FB_ROTATE_CW) | 803 | else if (rotation == FB_ROTATE_CW) |
804 | offset -= var->xoffset * fix->line_length + | 804 | offset -= var->xoffset * fix->line_length + |
805 | var->yoffset * (var->bits_per_pixel >> 3); | 805 | var->yoffset * (var->bits_per_pixel >> 3); |
806 | else if (rotation == FB_ROTATE_CCW) | 806 | else if (rotation == FB_ROTATE_CCW) |
807 | offset += var->xoffset * fix->line_length + | 807 | offset += var->xoffset * fix->line_length + |
808 | var->yoffset * (var->bits_per_pixel >> 3); | 808 | var->yoffset * (var->bits_per_pixel >> 3); |
809 | 809 | ||
810 | return offset; | 810 | return offset; |
811 | } | 811 | } |
812 | 812 | ||
813 | static void omapfb_calc_addr(const struct omapfb_info *ofbi, | 813 | static void omapfb_calc_addr(const struct omapfb_info *ofbi, |
814 | const struct fb_var_screeninfo *var, | 814 | const struct fb_var_screeninfo *var, |
815 | const struct fb_fix_screeninfo *fix, | 815 | const struct fb_fix_screeninfo *fix, |
816 | int rotation, u32 *paddr) | 816 | int rotation, u32 *paddr) |
817 | { | 817 | { |
818 | u32 data_start_p; | 818 | u32 data_start_p; |
819 | int offset; | 819 | int offset; |
820 | 820 | ||
821 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) | 821 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) |
822 | data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation); | 822 | data_start_p = omapfb_get_region_rot_paddr(ofbi, rotation); |
823 | else | 823 | else |
824 | data_start_p = omapfb_get_region_paddr(ofbi); | 824 | data_start_p = omapfb_get_region_paddr(ofbi); |
825 | 825 | ||
826 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) | 826 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) |
827 | offset = calc_rotation_offset_vrfb(var, fix, rotation); | 827 | offset = calc_rotation_offset_vrfb(var, fix, rotation); |
828 | else | 828 | else |
829 | offset = calc_rotation_offset_dma(var, fix, rotation); | 829 | offset = calc_rotation_offset_dma(var, fix, rotation); |
830 | 830 | ||
831 | data_start_p += offset; | 831 | data_start_p += offset; |
832 | 832 | ||
833 | if (offset) | 833 | if (offset) |
834 | DBG("offset %d, %d = %d\n", | 834 | DBG("offset %d, %d = %d\n", |
835 | var->xoffset, var->yoffset, offset); | 835 | var->xoffset, var->yoffset, offset); |
836 | 836 | ||
837 | DBG("paddr %x\n", data_start_p); | 837 | DBG("paddr %x\n", data_start_p); |
838 | 838 | ||
839 | *paddr = data_start_p; | 839 | *paddr = data_start_p; |
840 | } | 840 | } |
841 | 841 | ||
842 | /* setup overlay according to the fb */ | 842 | /* setup overlay according to the fb */ |
843 | int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, | 843 | int omapfb_setup_overlay(struct fb_info *fbi, struct omap_overlay *ovl, |
844 | u16 posx, u16 posy, u16 outw, u16 outh) | 844 | u16 posx, u16 posy, u16 outw, u16 outh) |
845 | { | 845 | { |
846 | int r = 0; | 846 | int r = 0; |
847 | struct omapfb_info *ofbi = FB2OFB(fbi); | 847 | struct omapfb_info *ofbi = FB2OFB(fbi); |
848 | struct fb_var_screeninfo *var = &fbi->var; | 848 | struct fb_var_screeninfo *var = &fbi->var; |
849 | struct fb_fix_screeninfo *fix = &fbi->fix; | 849 | struct fb_fix_screeninfo *fix = &fbi->fix; |
850 | enum omap_color_mode mode = 0; | 850 | enum omap_color_mode mode = 0; |
851 | u32 data_start_p = 0; | 851 | u32 data_start_p = 0; |
852 | struct omap_overlay_info info; | 852 | struct omap_overlay_info info; |
853 | int xres, yres; | 853 | int xres, yres; |
854 | int screen_width; | 854 | int screen_width; |
855 | int mirror; | 855 | int mirror; |
856 | int rotation = var->rotate; | 856 | int rotation = var->rotate; |
857 | int i; | 857 | int i; |
858 | 858 | ||
859 | WARN_ON(!atomic_read(&ofbi->region->lock_count)); | 859 | WARN_ON(!atomic_read(&ofbi->region->lock_count)); |
860 | 860 | ||
861 | for (i = 0; i < ofbi->num_overlays; i++) { | 861 | for (i = 0; i < ofbi->num_overlays; i++) { |
862 | if (ovl != ofbi->overlays[i]) | 862 | if (ovl != ofbi->overlays[i]) |
863 | continue; | 863 | continue; |
864 | 864 | ||
865 | rotation = (rotation + ofbi->rotation[i]) % 4; | 865 | rotation = (rotation + ofbi->rotation[i]) % 4; |
866 | break; | 866 | break; |
867 | } | 867 | } |
868 | 868 | ||
869 | DBG("setup_overlay %d, posx %d, posy %d, outw %d, outh %d\n", ofbi->id, | 869 | DBG("setup_overlay %d, posx %d, posy %d, outw %d, outh %d\n", ofbi->id, |
870 | posx, posy, outw, outh); | 870 | posx, posy, outw, outh); |
871 | 871 | ||
872 | if (rotation == FB_ROTATE_CW || rotation == FB_ROTATE_CCW) { | 872 | if (rotation == FB_ROTATE_CW || rotation == FB_ROTATE_CCW) { |
873 | xres = var->yres; | 873 | xres = var->yres; |
874 | yres = var->xres; | 874 | yres = var->xres; |
875 | } else { | 875 | } else { |
876 | xres = var->xres; | 876 | xres = var->xres; |
877 | yres = var->yres; | 877 | yres = var->yres; |
878 | } | 878 | } |
879 | 879 | ||
880 | if (ofbi->region->size) | 880 | if (ofbi->region->size) |
881 | omapfb_calc_addr(ofbi, var, fix, rotation, &data_start_p); | 881 | omapfb_calc_addr(ofbi, var, fix, rotation, &data_start_p); |
882 | 882 | ||
883 | r = fb_mode_to_dss_mode(var, &mode); | 883 | r = fb_mode_to_dss_mode(var, &mode); |
884 | if (r) { | 884 | if (r) { |
885 | DBG("fb_mode_to_dss_mode failed"); | 885 | DBG("fb_mode_to_dss_mode failed"); |
886 | goto err; | 886 | goto err; |
887 | } | 887 | } |
888 | 888 | ||
889 | switch (var->nonstd) { | 889 | switch (var->nonstd) { |
890 | case OMAPFB_COLOR_YUV422: | 890 | case OMAPFB_COLOR_YUV422: |
891 | case OMAPFB_COLOR_YUY422: | 891 | case OMAPFB_COLOR_YUY422: |
892 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { | 892 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { |
893 | screen_width = fix->line_length | 893 | screen_width = fix->line_length |
894 | / (var->bits_per_pixel >> 2); | 894 | / (var->bits_per_pixel >> 2); |
895 | break; | 895 | break; |
896 | } | 896 | } |
897 | default: | 897 | default: |
898 | screen_width = fix->line_length / (var->bits_per_pixel >> 3); | 898 | screen_width = fix->line_length / (var->bits_per_pixel >> 3); |
899 | break; | 899 | break; |
900 | } | 900 | } |
901 | 901 | ||
902 | ovl->get_overlay_info(ovl, &info); | 902 | ovl->get_overlay_info(ovl, &info); |
903 | 903 | ||
904 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) | 904 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) |
905 | mirror = 0; | 905 | mirror = 0; |
906 | else | 906 | else |
907 | mirror = ofbi->mirror; | 907 | mirror = ofbi->mirror; |
908 | 908 | ||
909 | info.paddr = data_start_p; | 909 | info.paddr = data_start_p; |
910 | info.screen_width = screen_width; | 910 | info.screen_width = screen_width; |
911 | info.width = xres; | 911 | info.width = xres; |
912 | info.height = yres; | 912 | info.height = yres; |
913 | info.color_mode = mode; | 913 | info.color_mode = mode; |
914 | info.rotation_type = ofbi->rotation_type; | 914 | info.rotation_type = ofbi->rotation_type; |
915 | info.rotation = rotation; | 915 | info.rotation = rotation; |
916 | info.mirror = mirror; | 916 | info.mirror = mirror; |
917 | 917 | ||
918 | info.pos_x = posx; | 918 | info.pos_x = posx; |
919 | info.pos_y = posy; | 919 | info.pos_y = posy; |
920 | info.out_width = outw; | 920 | info.out_width = outw; |
921 | info.out_height = outh; | 921 | info.out_height = outh; |
922 | 922 | ||
923 | r = ovl->set_overlay_info(ovl, &info); | 923 | r = ovl->set_overlay_info(ovl, &info); |
924 | if (r) { | 924 | if (r) { |
925 | DBG("ovl->setup_overlay_info failed\n"); | 925 | DBG("ovl->setup_overlay_info failed\n"); |
926 | goto err; | 926 | goto err; |
927 | } | 927 | } |
928 | 928 | ||
929 | return 0; | 929 | return 0; |
930 | 930 | ||
931 | err: | 931 | err: |
932 | DBG("setup_overlay failed\n"); | 932 | DBG("setup_overlay failed\n"); |
933 | return r; | 933 | return r; |
934 | } | 934 | } |
935 | 935 | ||
936 | /* apply var to the overlay */ | 936 | /* apply var to the overlay */ |
937 | int omapfb_apply_changes(struct fb_info *fbi, int init) | 937 | int omapfb_apply_changes(struct fb_info *fbi, int init) |
938 | { | 938 | { |
939 | int r = 0; | 939 | int r = 0; |
940 | struct omapfb_info *ofbi = FB2OFB(fbi); | 940 | struct omapfb_info *ofbi = FB2OFB(fbi); |
941 | struct fb_var_screeninfo *var = &fbi->var; | 941 | struct fb_var_screeninfo *var = &fbi->var; |
942 | struct omap_overlay *ovl; | 942 | struct omap_overlay *ovl; |
943 | u16 posx, posy; | 943 | u16 posx, posy; |
944 | u16 outw, outh; | 944 | u16 outw, outh; |
945 | int i; | 945 | int i; |
946 | 946 | ||
947 | #ifdef DEBUG | 947 | #ifdef DEBUG |
948 | if (omapfb_test_pattern) | 948 | if (omapfb_test_pattern) |
949 | fill_fb(fbi); | 949 | fill_fb(fbi); |
950 | #endif | 950 | #endif |
951 | 951 | ||
952 | WARN_ON(!atomic_read(&ofbi->region->lock_count)); | 952 | WARN_ON(!atomic_read(&ofbi->region->lock_count)); |
953 | 953 | ||
954 | for (i = 0; i < ofbi->num_overlays; i++) { | 954 | for (i = 0; i < ofbi->num_overlays; i++) { |
955 | ovl = ofbi->overlays[i]; | 955 | ovl = ofbi->overlays[i]; |
956 | 956 | ||
957 | DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id); | 957 | DBG("apply_changes, fb %d, ovl %d\n", ofbi->id, ovl->id); |
958 | 958 | ||
959 | if (ofbi->region->size == 0) { | 959 | if (ofbi->region->size == 0) { |
960 | /* the fb is not available. disable the overlay */ | 960 | /* the fb is not available. disable the overlay */ |
961 | omapfb_overlay_enable(ovl, 0); | 961 | omapfb_overlay_enable(ovl, 0); |
962 | if (!init && ovl->manager) | 962 | if (!init && ovl->manager) |
963 | ovl->manager->apply(ovl->manager); | 963 | ovl->manager->apply(ovl->manager); |
964 | continue; | 964 | continue; |
965 | } | 965 | } |
966 | 966 | ||
967 | if (init || (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { | 967 | if (init || (ovl->caps & OMAP_DSS_OVL_CAP_SCALE) == 0) { |
968 | int rotation = (var->rotate + ofbi->rotation[i]) % 4; | 968 | int rotation = (var->rotate + ofbi->rotation[i]) % 4; |
969 | if (rotation == FB_ROTATE_CW || | 969 | if (rotation == FB_ROTATE_CW || |
970 | rotation == FB_ROTATE_CCW) { | 970 | rotation == FB_ROTATE_CCW) { |
971 | outw = var->yres; | 971 | outw = var->yres; |
972 | outh = var->xres; | 972 | outh = var->xres; |
973 | } else { | 973 | } else { |
974 | outw = var->xres; | 974 | outw = var->xres; |
975 | outh = var->yres; | 975 | outh = var->yres; |
976 | } | 976 | } |
977 | } else { | 977 | } else { |
978 | struct omap_overlay_info info; | 978 | struct omap_overlay_info info; |
979 | ovl->get_overlay_info(ovl, &info); | 979 | ovl->get_overlay_info(ovl, &info); |
980 | outw = info.out_width; | 980 | outw = info.out_width; |
981 | outh = info.out_height; | 981 | outh = info.out_height; |
982 | } | 982 | } |
983 | 983 | ||
984 | if (init) { | 984 | if (init) { |
985 | posx = 0; | 985 | posx = 0; |
986 | posy = 0; | 986 | posy = 0; |
987 | } else { | 987 | } else { |
988 | struct omap_overlay_info info; | 988 | struct omap_overlay_info info; |
989 | ovl->get_overlay_info(ovl, &info); | 989 | ovl->get_overlay_info(ovl, &info); |
990 | posx = info.pos_x; | 990 | posx = info.pos_x; |
991 | posy = info.pos_y; | 991 | posy = info.pos_y; |
992 | } | 992 | } |
993 | 993 | ||
994 | r = omapfb_setup_overlay(fbi, ovl, posx, posy, outw, outh); | 994 | r = omapfb_setup_overlay(fbi, ovl, posx, posy, outw, outh); |
995 | if (r) | 995 | if (r) |
996 | goto err; | 996 | goto err; |
997 | 997 | ||
998 | if (!init && ovl->manager) | 998 | if (!init && ovl->manager) |
999 | ovl->manager->apply(ovl->manager); | 999 | ovl->manager->apply(ovl->manager); |
1000 | } | 1000 | } |
1001 | return 0; | 1001 | return 0; |
1002 | err: | 1002 | err: |
1003 | DBG("apply_changes failed\n"); | 1003 | DBG("apply_changes failed\n"); |
1004 | return r; | 1004 | return r; |
1005 | } | 1005 | } |
1006 | 1006 | ||
1007 | /* checks var and eventually tweaks it to something supported, | 1007 | /* checks var and eventually tweaks it to something supported, |
1008 | * DO NOT MODIFY PAR */ | 1008 | * DO NOT MODIFY PAR */ |
1009 | static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) | 1009 | static int omapfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fbi) |
1010 | { | 1010 | { |
1011 | struct omapfb_info *ofbi = FB2OFB(fbi); | 1011 | struct omapfb_info *ofbi = FB2OFB(fbi); |
1012 | int r; | 1012 | int r; |
1013 | 1013 | ||
1014 | DBG("check_var(%d)\n", FB2OFB(fbi)->id); | 1014 | DBG("check_var(%d)\n", FB2OFB(fbi)->id); |
1015 | 1015 | ||
1016 | omapfb_get_mem_region(ofbi->region); | 1016 | omapfb_get_mem_region(ofbi->region); |
1017 | 1017 | ||
1018 | r = check_fb_var(fbi, var); | 1018 | r = check_fb_var(fbi, var); |
1019 | 1019 | ||
1020 | omapfb_put_mem_region(ofbi->region); | 1020 | omapfb_put_mem_region(ofbi->region); |
1021 | 1021 | ||
1022 | return r; | 1022 | return r; |
1023 | } | 1023 | } |
1024 | 1024 | ||
1025 | /* set the video mode according to info->var */ | 1025 | /* set the video mode according to info->var */ |
1026 | static int omapfb_set_par(struct fb_info *fbi) | 1026 | static int omapfb_set_par(struct fb_info *fbi) |
1027 | { | 1027 | { |
1028 | struct omapfb_info *ofbi = FB2OFB(fbi); | 1028 | struct omapfb_info *ofbi = FB2OFB(fbi); |
1029 | int r; | 1029 | int r; |
1030 | 1030 | ||
1031 | DBG("set_par(%d)\n", FB2OFB(fbi)->id); | 1031 | DBG("set_par(%d)\n", FB2OFB(fbi)->id); |
1032 | 1032 | ||
1033 | omapfb_get_mem_region(ofbi->region); | 1033 | omapfb_get_mem_region(ofbi->region); |
1034 | 1034 | ||
1035 | set_fb_fix(fbi); | 1035 | set_fb_fix(fbi); |
1036 | 1036 | ||
1037 | r = setup_vrfb_rotation(fbi); | 1037 | r = setup_vrfb_rotation(fbi); |
1038 | if (r) | 1038 | if (r) |
1039 | goto out; | 1039 | goto out; |
1040 | 1040 | ||
1041 | r = omapfb_apply_changes(fbi, 0); | 1041 | r = omapfb_apply_changes(fbi, 0); |
1042 | 1042 | ||
1043 | out: | 1043 | out: |
1044 | omapfb_put_mem_region(ofbi->region); | 1044 | omapfb_put_mem_region(ofbi->region); |
1045 | 1045 | ||
1046 | return r; | 1046 | return r; |
1047 | } | 1047 | } |
1048 | 1048 | ||
1049 | static int omapfb_pan_display(struct fb_var_screeninfo *var, | 1049 | static int omapfb_pan_display(struct fb_var_screeninfo *var, |
1050 | struct fb_info *fbi) | 1050 | struct fb_info *fbi) |
1051 | { | 1051 | { |
1052 | struct omapfb_info *ofbi = FB2OFB(fbi); | 1052 | struct omapfb_info *ofbi = FB2OFB(fbi); |
1053 | struct fb_var_screeninfo new_var; | 1053 | struct fb_var_screeninfo new_var; |
1054 | int r; | 1054 | int r; |
1055 | 1055 | ||
1056 | DBG("pan_display(%d)\n", FB2OFB(fbi)->id); | 1056 | DBG("pan_display(%d)\n", FB2OFB(fbi)->id); |
1057 | 1057 | ||
1058 | if (var->xoffset == fbi->var.xoffset && | 1058 | if (var->xoffset == fbi->var.xoffset && |
1059 | var->yoffset == fbi->var.yoffset) | 1059 | var->yoffset == fbi->var.yoffset) |
1060 | return 0; | 1060 | return 0; |
1061 | 1061 | ||
1062 | new_var = fbi->var; | 1062 | new_var = fbi->var; |
1063 | new_var.xoffset = var->xoffset; | 1063 | new_var.xoffset = var->xoffset; |
1064 | new_var.yoffset = var->yoffset; | 1064 | new_var.yoffset = var->yoffset; |
1065 | 1065 | ||
1066 | fbi->var = new_var; | 1066 | fbi->var = new_var; |
1067 | 1067 | ||
1068 | omapfb_get_mem_region(ofbi->region); | 1068 | omapfb_get_mem_region(ofbi->region); |
1069 | 1069 | ||
1070 | r = omapfb_apply_changes(fbi, 0); | 1070 | r = omapfb_apply_changes(fbi, 0); |
1071 | 1071 | ||
1072 | omapfb_put_mem_region(ofbi->region); | 1072 | omapfb_put_mem_region(ofbi->region); |
1073 | 1073 | ||
1074 | return r; | 1074 | return r; |
1075 | } | 1075 | } |
1076 | 1076 | ||
1077 | static void mmap_user_open(struct vm_area_struct *vma) | 1077 | static void mmap_user_open(struct vm_area_struct *vma) |
1078 | { | 1078 | { |
1079 | struct omapfb2_mem_region *rg = vma->vm_private_data; | 1079 | struct omapfb2_mem_region *rg = vma->vm_private_data; |
1080 | 1080 | ||
1081 | omapfb_get_mem_region(rg); | 1081 | omapfb_get_mem_region(rg); |
1082 | atomic_inc(&rg->map_count); | 1082 | atomic_inc(&rg->map_count); |
1083 | omapfb_put_mem_region(rg); | 1083 | omapfb_put_mem_region(rg); |
1084 | } | 1084 | } |
1085 | 1085 | ||
1086 | static void mmap_user_close(struct vm_area_struct *vma) | 1086 | static void mmap_user_close(struct vm_area_struct *vma) |
1087 | { | 1087 | { |
1088 | struct omapfb2_mem_region *rg = vma->vm_private_data; | 1088 | struct omapfb2_mem_region *rg = vma->vm_private_data; |
1089 | 1089 | ||
1090 | omapfb_get_mem_region(rg); | 1090 | omapfb_get_mem_region(rg); |
1091 | atomic_dec(&rg->map_count); | 1091 | atomic_dec(&rg->map_count); |
1092 | omapfb_put_mem_region(rg); | 1092 | omapfb_put_mem_region(rg); |
1093 | } | 1093 | } |
1094 | 1094 | ||
1095 | static struct vm_operations_struct mmap_user_ops = { | 1095 | static struct vm_operations_struct mmap_user_ops = { |
1096 | .open = mmap_user_open, | 1096 | .open = mmap_user_open, |
1097 | .close = mmap_user_close, | 1097 | .close = mmap_user_close, |
1098 | }; | 1098 | }; |
1099 | 1099 | ||
1100 | static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) | 1100 | static int omapfb_mmap(struct fb_info *fbi, struct vm_area_struct *vma) |
1101 | { | 1101 | { |
1102 | struct omapfb_info *ofbi = FB2OFB(fbi); | 1102 | struct omapfb_info *ofbi = FB2OFB(fbi); |
1103 | struct fb_fix_screeninfo *fix = &fbi->fix; | 1103 | struct fb_fix_screeninfo *fix = &fbi->fix; |
1104 | struct omapfb2_mem_region *rg; | 1104 | struct omapfb2_mem_region *rg; |
1105 | unsigned long off; | 1105 | unsigned long off; |
1106 | unsigned long start; | 1106 | unsigned long start; |
1107 | u32 len; | 1107 | u32 len; |
1108 | int r = -EINVAL; | 1108 | int r = -EINVAL; |
1109 | 1109 | ||
1110 | if (vma->vm_end - vma->vm_start == 0) | 1110 | if (vma->vm_end - vma->vm_start == 0) |
1111 | return 0; | 1111 | return 0; |
1112 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) | 1112 | if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) |
1113 | return -EINVAL; | 1113 | return -EINVAL; |
1114 | off = vma->vm_pgoff << PAGE_SHIFT; | 1114 | off = vma->vm_pgoff << PAGE_SHIFT; |
1115 | 1115 | ||
1116 | rg = omapfb_get_mem_region(ofbi->region); | 1116 | rg = omapfb_get_mem_region(ofbi->region); |
1117 | 1117 | ||
1118 | start = omapfb_get_region_paddr(ofbi); | 1118 | start = omapfb_get_region_paddr(ofbi); |
1119 | len = fix->smem_len; | 1119 | len = fix->smem_len; |
1120 | if (off >= len) | 1120 | if (off >= len) |
1121 | goto error; | 1121 | goto error; |
1122 | if ((vma->vm_end - vma->vm_start + off) > len) | 1122 | if ((vma->vm_end - vma->vm_start + off) > len) |
1123 | goto error; | 1123 | goto error; |
1124 | 1124 | ||
1125 | off += start; | 1125 | off += start; |
1126 | 1126 | ||
1127 | DBG("user mmap region start %lx, len %d, off %lx\n", start, len, off); | 1127 | DBG("user mmap region start %lx, len %d, off %lx\n", start, len, off); |
1128 | 1128 | ||
1129 | vma->vm_pgoff = off >> PAGE_SHIFT; | 1129 | vma->vm_pgoff = off >> PAGE_SHIFT; |
1130 | vma->vm_flags |= VM_IO | VM_RESERVED; | 1130 | vma->vm_flags |= VM_IO | VM_RESERVED; |
1131 | vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); | 1131 | vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); |
1132 | vma->vm_ops = &mmap_user_ops; | 1132 | vma->vm_ops = &mmap_user_ops; |
1133 | vma->vm_private_data = rg; | 1133 | vma->vm_private_data = rg; |
1134 | if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, | 1134 | if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, |
1135 | vma->vm_end - vma->vm_start, | 1135 | vma->vm_end - vma->vm_start, |
1136 | vma->vm_page_prot)) { | 1136 | vma->vm_page_prot)) { |
1137 | r = -EAGAIN; | 1137 | r = -EAGAIN; |
1138 | goto error; | 1138 | goto error; |
1139 | } | 1139 | } |
1140 | 1140 | ||
1141 | /* vm_ops.open won't be called for mmap itself. */ | 1141 | /* vm_ops.open won't be called for mmap itself. */ |
1142 | atomic_inc(&rg->map_count); | 1142 | atomic_inc(&rg->map_count); |
1143 | 1143 | ||
1144 | omapfb_put_mem_region(rg); | 1144 | omapfb_put_mem_region(rg); |
1145 | 1145 | ||
1146 | return 0; | 1146 | return 0; |
1147 | 1147 | ||
1148 | error: | 1148 | error: |
1149 | omapfb_put_mem_region(ofbi->region); | 1149 | omapfb_put_mem_region(ofbi->region); |
1150 | 1150 | ||
1151 | return r; | 1151 | return r; |
1152 | } | 1152 | } |
1153 | 1153 | ||
1154 | /* Store a single color palette entry into a pseudo palette or the hardware | 1154 | /* Store a single color palette entry into a pseudo palette or the hardware |
1155 | * palette if one is available. For now we support only 16bpp and thus store | 1155 | * palette if one is available. For now we support only 16bpp and thus store |
1156 | * the entry only to the pseudo palette. | 1156 | * the entry only to the pseudo palette. |
1157 | */ | 1157 | */ |
1158 | static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green, | 1158 | static int _setcolreg(struct fb_info *fbi, u_int regno, u_int red, u_int green, |
1159 | u_int blue, u_int transp, int update_hw_pal) | 1159 | u_int blue, u_int transp, int update_hw_pal) |
1160 | { | 1160 | { |
1161 | /*struct omapfb_info *ofbi = FB2OFB(fbi);*/ | 1161 | /*struct omapfb_info *ofbi = FB2OFB(fbi);*/ |
1162 | /*struct omapfb2_device *fbdev = ofbi->fbdev;*/ | 1162 | /*struct omapfb2_device *fbdev = ofbi->fbdev;*/ |
1163 | struct fb_var_screeninfo *var = &fbi->var; | 1163 | struct fb_var_screeninfo *var = &fbi->var; |
1164 | int r = 0; | 1164 | int r = 0; |
1165 | 1165 | ||
1166 | enum omapfb_color_format mode = OMAPFB_COLOR_RGB24U; /* XXX */ | 1166 | enum omapfb_color_format mode = OMAPFB_COLOR_RGB24U; /* XXX */ |
1167 | 1167 | ||
1168 | /*switch (plane->color_mode) {*/ | 1168 | /*switch (plane->color_mode) {*/ |
1169 | switch (mode) { | 1169 | switch (mode) { |
1170 | case OMAPFB_COLOR_YUV422: | 1170 | case OMAPFB_COLOR_YUV422: |
1171 | case OMAPFB_COLOR_YUV420: | 1171 | case OMAPFB_COLOR_YUV420: |
1172 | case OMAPFB_COLOR_YUY422: | 1172 | case OMAPFB_COLOR_YUY422: |
1173 | r = -EINVAL; | 1173 | r = -EINVAL; |
1174 | break; | 1174 | break; |
1175 | case OMAPFB_COLOR_CLUT_8BPP: | 1175 | case OMAPFB_COLOR_CLUT_8BPP: |
1176 | case OMAPFB_COLOR_CLUT_4BPP: | 1176 | case OMAPFB_COLOR_CLUT_4BPP: |
1177 | case OMAPFB_COLOR_CLUT_2BPP: | 1177 | case OMAPFB_COLOR_CLUT_2BPP: |
1178 | case OMAPFB_COLOR_CLUT_1BPP: | 1178 | case OMAPFB_COLOR_CLUT_1BPP: |
1179 | /* | 1179 | /* |
1180 | if (fbdev->ctrl->setcolreg) | 1180 | if (fbdev->ctrl->setcolreg) |
1181 | r = fbdev->ctrl->setcolreg(regno, red, green, blue, | 1181 | r = fbdev->ctrl->setcolreg(regno, red, green, blue, |
1182 | transp, update_hw_pal); | 1182 | transp, update_hw_pal); |
1183 | */ | 1183 | */ |
1184 | /* Fallthrough */ | 1184 | /* Fallthrough */ |
1185 | r = -EINVAL; | 1185 | r = -EINVAL; |
1186 | break; | 1186 | break; |
1187 | case OMAPFB_COLOR_RGB565: | 1187 | case OMAPFB_COLOR_RGB565: |
1188 | case OMAPFB_COLOR_RGB444: | 1188 | case OMAPFB_COLOR_RGB444: |
1189 | case OMAPFB_COLOR_RGB24P: | 1189 | case OMAPFB_COLOR_RGB24P: |
1190 | case OMAPFB_COLOR_RGB24U: | 1190 | case OMAPFB_COLOR_RGB24U: |
1191 | if (r != 0) | 1191 | if (r != 0) |
1192 | break; | 1192 | break; |
1193 | 1193 | ||
1194 | if (regno < 16) { | 1194 | if (regno < 16) { |
1195 | u16 pal; | 1195 | u32 pal; |
1196 | pal = ((red >> (16 - var->red.length)) << | 1196 | pal = ((red >> (16 - var->red.length)) << |
1197 | var->red.offset) | | 1197 | var->red.offset) | |
1198 | ((green >> (16 - var->green.length)) << | 1198 | ((green >> (16 - var->green.length)) << |
1199 | var->green.offset) | | 1199 | var->green.offset) | |
1200 | (blue >> (16 - var->blue.length)); | 1200 | (blue >> (16 - var->blue.length)); |
1201 | ((u32 *)(fbi->pseudo_palette))[regno] = pal; | 1201 | ((u32 *)(fbi->pseudo_palette))[regno] = pal; |
1202 | } | 1202 | } |
1203 | break; | 1203 | break; |
1204 | default: | 1204 | default: |
1205 | BUG(); | 1205 | BUG(); |
1206 | } | 1206 | } |
1207 | return r; | 1207 | return r; |
1208 | } | 1208 | } |
1209 | 1209 | ||
1210 | static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, | 1210 | static int omapfb_setcolreg(u_int regno, u_int red, u_int green, u_int blue, |
1211 | u_int transp, struct fb_info *info) | 1211 | u_int transp, struct fb_info *info) |
1212 | { | 1212 | { |
1213 | DBG("setcolreg\n"); | 1213 | DBG("setcolreg\n"); |
1214 | 1214 | ||
1215 | return _setcolreg(info, regno, red, green, blue, transp, 1); | 1215 | return _setcolreg(info, regno, red, green, blue, transp, 1); |
1216 | } | 1216 | } |
1217 | 1217 | ||
1218 | static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info) | 1218 | static int omapfb_setcmap(struct fb_cmap *cmap, struct fb_info *info) |
1219 | { | 1219 | { |
1220 | int count, index, r; | 1220 | int count, index, r; |
1221 | u16 *red, *green, *blue, *transp; | 1221 | u16 *red, *green, *blue, *transp; |
1222 | u16 trans = 0xffff; | 1222 | u16 trans = 0xffff; |
1223 | 1223 | ||
1224 | DBG("setcmap\n"); | 1224 | DBG("setcmap\n"); |
1225 | 1225 | ||
1226 | red = cmap->red; | 1226 | red = cmap->red; |
1227 | green = cmap->green; | 1227 | green = cmap->green; |
1228 | blue = cmap->blue; | 1228 | blue = cmap->blue; |
1229 | transp = cmap->transp; | 1229 | transp = cmap->transp; |
1230 | index = cmap->start; | 1230 | index = cmap->start; |
1231 | 1231 | ||
1232 | for (count = 0; count < cmap->len; count++) { | 1232 | for (count = 0; count < cmap->len; count++) { |
1233 | if (transp) | 1233 | if (transp) |
1234 | trans = *transp++; | 1234 | trans = *transp++; |
1235 | r = _setcolreg(info, index++, *red++, *green++, *blue++, trans, | 1235 | r = _setcolreg(info, index++, *red++, *green++, *blue++, trans, |
1236 | count == cmap->len - 1); | 1236 | count == cmap->len - 1); |
1237 | if (r != 0) | 1237 | if (r != 0) |
1238 | return r; | 1238 | return r; |
1239 | } | 1239 | } |
1240 | 1240 | ||
1241 | return 0; | 1241 | return 0; |
1242 | } | 1242 | } |
1243 | 1243 | ||
1244 | static int omapfb_blank(int blank, struct fb_info *fbi) | 1244 | static int omapfb_blank(int blank, struct fb_info *fbi) |
1245 | { | 1245 | { |
1246 | struct omapfb_info *ofbi = FB2OFB(fbi); | 1246 | struct omapfb_info *ofbi = FB2OFB(fbi); |
1247 | struct omapfb2_device *fbdev = ofbi->fbdev; | 1247 | struct omapfb2_device *fbdev = ofbi->fbdev; |
1248 | struct omap_dss_device *display = fb2display(fbi); | 1248 | struct omap_dss_device *display = fb2display(fbi); |
1249 | struct omapfb_display_data *d; | 1249 | struct omapfb_display_data *d; |
1250 | int r = 0; | 1250 | int r = 0; |
1251 | 1251 | ||
1252 | if (!display) | 1252 | if (!display) |
1253 | return -EINVAL; | 1253 | return -EINVAL; |
1254 | 1254 | ||
1255 | omapfb_lock(fbdev); | 1255 | omapfb_lock(fbdev); |
1256 | 1256 | ||
1257 | d = get_display_data(fbdev, display); | 1257 | d = get_display_data(fbdev, display); |
1258 | 1258 | ||
1259 | switch (blank) { | 1259 | switch (blank) { |
1260 | case FB_BLANK_UNBLANK: | 1260 | case FB_BLANK_UNBLANK: |
1261 | if (display->state != OMAP_DSS_DISPLAY_SUSPENDED) | 1261 | if (display->state != OMAP_DSS_DISPLAY_SUSPENDED) |
1262 | goto exit; | 1262 | goto exit; |
1263 | 1263 | ||
1264 | if (display->driver->resume) | 1264 | if (display->driver->resume) |
1265 | r = display->driver->resume(display); | 1265 | r = display->driver->resume(display); |
1266 | 1266 | ||
1267 | if ((display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) && | 1267 | if ((display->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) && |
1268 | d->update_mode == OMAPFB_AUTO_UPDATE && | 1268 | d->update_mode == OMAPFB_AUTO_UPDATE && |
1269 | !d->auto_update_work_enabled) | 1269 | !d->auto_update_work_enabled) |
1270 | omapfb_start_auto_update(fbdev, display); | 1270 | omapfb_start_auto_update(fbdev, display); |
1271 | 1271 | ||
1272 | break; | 1272 | break; |
1273 | 1273 | ||
1274 | case FB_BLANK_NORMAL: | 1274 | case FB_BLANK_NORMAL: |
1275 | /* FB_BLANK_NORMAL could be implemented. | 1275 | /* FB_BLANK_NORMAL could be implemented. |
1276 | * Needs DSS additions. */ | 1276 | * Needs DSS additions. */ |
1277 | case FB_BLANK_VSYNC_SUSPEND: | 1277 | case FB_BLANK_VSYNC_SUSPEND: |
1278 | case FB_BLANK_HSYNC_SUSPEND: | 1278 | case FB_BLANK_HSYNC_SUSPEND: |
1279 | case FB_BLANK_POWERDOWN: | 1279 | case FB_BLANK_POWERDOWN: |
1280 | if (display->state != OMAP_DSS_DISPLAY_ACTIVE) | 1280 | if (display->state != OMAP_DSS_DISPLAY_ACTIVE) |
1281 | goto exit; | 1281 | goto exit; |
1282 | 1282 | ||
1283 | if (d->auto_update_work_enabled) | 1283 | if (d->auto_update_work_enabled) |
1284 | omapfb_stop_auto_update(fbdev, display); | 1284 | omapfb_stop_auto_update(fbdev, display); |
1285 | 1285 | ||
1286 | if (display->driver->suspend) | 1286 | if (display->driver->suspend) |
1287 | r = display->driver->suspend(display); | 1287 | r = display->driver->suspend(display); |
1288 | 1288 | ||
1289 | break; | 1289 | break; |
1290 | 1290 | ||
1291 | default: | 1291 | default: |
1292 | r = -EINVAL; | 1292 | r = -EINVAL; |
1293 | } | 1293 | } |
1294 | 1294 | ||
1295 | exit: | 1295 | exit: |
1296 | omapfb_unlock(fbdev); | 1296 | omapfb_unlock(fbdev); |
1297 | 1297 | ||
1298 | return r; | 1298 | return r; |
1299 | } | 1299 | } |
1300 | 1300 | ||
1301 | #if 0 | 1301 | #if 0 |
1302 | /* XXX fb_read and fb_write are needed for VRFB */ | 1302 | /* XXX fb_read and fb_write are needed for VRFB */ |
1303 | ssize_t omapfb_write(struct fb_info *info, const char __user *buf, | 1303 | ssize_t omapfb_write(struct fb_info *info, const char __user *buf, |
1304 | size_t count, loff_t *ppos) | 1304 | size_t count, loff_t *ppos) |
1305 | { | 1305 | { |
1306 | DBG("omapfb_write %d, %lu\n", count, (unsigned long)*ppos); | 1306 | DBG("omapfb_write %d, %lu\n", count, (unsigned long)*ppos); |
1307 | /* XXX needed for VRFB */ | 1307 | /* XXX needed for VRFB */ |
1308 | return count; | 1308 | return count; |
1309 | } | 1309 | } |
1310 | #endif | 1310 | #endif |
1311 | 1311 | ||
1312 | static struct fb_ops omapfb_ops = { | 1312 | static struct fb_ops omapfb_ops = { |
1313 | .owner = THIS_MODULE, | 1313 | .owner = THIS_MODULE, |
1314 | .fb_open = omapfb_open, | 1314 | .fb_open = omapfb_open, |
1315 | .fb_release = omapfb_release, | 1315 | .fb_release = omapfb_release, |
1316 | .fb_fillrect = cfb_fillrect, | 1316 | .fb_fillrect = cfb_fillrect, |
1317 | .fb_copyarea = cfb_copyarea, | 1317 | .fb_copyarea = cfb_copyarea, |
1318 | .fb_imageblit = cfb_imageblit, | 1318 | .fb_imageblit = cfb_imageblit, |
1319 | .fb_blank = omapfb_blank, | 1319 | .fb_blank = omapfb_blank, |
1320 | .fb_ioctl = omapfb_ioctl, | 1320 | .fb_ioctl = omapfb_ioctl, |
1321 | .fb_check_var = omapfb_check_var, | 1321 | .fb_check_var = omapfb_check_var, |
1322 | .fb_set_par = omapfb_set_par, | 1322 | .fb_set_par = omapfb_set_par, |
1323 | .fb_pan_display = omapfb_pan_display, | 1323 | .fb_pan_display = omapfb_pan_display, |
1324 | .fb_mmap = omapfb_mmap, | 1324 | .fb_mmap = omapfb_mmap, |
1325 | .fb_setcolreg = omapfb_setcolreg, | 1325 | .fb_setcolreg = omapfb_setcolreg, |
1326 | .fb_setcmap = omapfb_setcmap, | 1326 | .fb_setcmap = omapfb_setcmap, |
1327 | /*.fb_write = omapfb_write,*/ | 1327 | /*.fb_write = omapfb_write,*/ |
1328 | }; | 1328 | }; |
1329 | 1329 | ||
1330 | static void omapfb_free_fbmem(struct fb_info *fbi) | 1330 | static void omapfb_free_fbmem(struct fb_info *fbi) |
1331 | { | 1331 | { |
1332 | struct omapfb_info *ofbi = FB2OFB(fbi); | 1332 | struct omapfb_info *ofbi = FB2OFB(fbi); |
1333 | struct omapfb2_device *fbdev = ofbi->fbdev; | 1333 | struct omapfb2_device *fbdev = ofbi->fbdev; |
1334 | struct omapfb2_mem_region *rg; | 1334 | struct omapfb2_mem_region *rg; |
1335 | 1335 | ||
1336 | rg = ofbi->region; | 1336 | rg = ofbi->region; |
1337 | 1337 | ||
1338 | WARN_ON(atomic_read(&rg->map_count)); | 1338 | WARN_ON(atomic_read(&rg->map_count)); |
1339 | 1339 | ||
1340 | if (rg->paddr) | 1340 | if (rg->paddr) |
1341 | if (omap_vram_free(rg->paddr, rg->size)) | 1341 | if (omap_vram_free(rg->paddr, rg->size)) |
1342 | dev_err(fbdev->dev, "VRAM FREE failed\n"); | 1342 | dev_err(fbdev->dev, "VRAM FREE failed\n"); |
1343 | 1343 | ||
1344 | if (rg->vaddr) | 1344 | if (rg->vaddr) |
1345 | iounmap(rg->vaddr); | 1345 | iounmap(rg->vaddr); |
1346 | 1346 | ||
1347 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { | 1347 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { |
1348 | /* unmap the 0 angle rotation */ | 1348 | /* unmap the 0 angle rotation */ |
1349 | if (rg->vrfb.vaddr[0]) { | 1349 | if (rg->vrfb.vaddr[0]) { |
1350 | iounmap(rg->vrfb.vaddr[0]); | 1350 | iounmap(rg->vrfb.vaddr[0]); |
1351 | omap_vrfb_release_ctx(&rg->vrfb); | 1351 | omap_vrfb_release_ctx(&rg->vrfb); |
1352 | rg->vrfb.vaddr[0] = NULL; | 1352 | rg->vrfb.vaddr[0] = NULL; |
1353 | } | 1353 | } |
1354 | } | 1354 | } |
1355 | 1355 | ||
1356 | rg->vaddr = NULL; | 1356 | rg->vaddr = NULL; |
1357 | rg->paddr = 0; | 1357 | rg->paddr = 0; |
1358 | rg->alloc = 0; | 1358 | rg->alloc = 0; |
1359 | rg->size = 0; | 1359 | rg->size = 0; |
1360 | } | 1360 | } |
1361 | 1361 | ||
1362 | static void clear_fb_info(struct fb_info *fbi) | 1362 | static void clear_fb_info(struct fb_info *fbi) |
1363 | { | 1363 | { |
1364 | memset(&fbi->var, 0, sizeof(fbi->var)); | 1364 | memset(&fbi->var, 0, sizeof(fbi->var)); |
1365 | memset(&fbi->fix, 0, sizeof(fbi->fix)); | 1365 | memset(&fbi->fix, 0, sizeof(fbi->fix)); |
1366 | strlcpy(fbi->fix.id, MODULE_NAME, sizeof(fbi->fix.id)); | 1366 | strlcpy(fbi->fix.id, MODULE_NAME, sizeof(fbi->fix.id)); |
1367 | } | 1367 | } |
1368 | 1368 | ||
1369 | static int omapfb_free_all_fbmem(struct omapfb2_device *fbdev) | 1369 | static int omapfb_free_all_fbmem(struct omapfb2_device *fbdev) |
1370 | { | 1370 | { |
1371 | int i; | 1371 | int i; |
1372 | 1372 | ||
1373 | DBG("free all fbmem\n"); | 1373 | DBG("free all fbmem\n"); |
1374 | 1374 | ||
1375 | for (i = 0; i < fbdev->num_fbs; i++) { | 1375 | for (i = 0; i < fbdev->num_fbs; i++) { |
1376 | struct fb_info *fbi = fbdev->fbs[i]; | 1376 | struct fb_info *fbi = fbdev->fbs[i]; |
1377 | omapfb_free_fbmem(fbi); | 1377 | omapfb_free_fbmem(fbi); |
1378 | clear_fb_info(fbi); | 1378 | clear_fb_info(fbi); |
1379 | } | 1379 | } |
1380 | 1380 | ||
1381 | return 0; | 1381 | return 0; |
1382 | } | 1382 | } |
1383 | 1383 | ||
1384 | static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, | 1384 | static int omapfb_alloc_fbmem(struct fb_info *fbi, unsigned long size, |
1385 | unsigned long paddr) | 1385 | unsigned long paddr) |
1386 | { | 1386 | { |
1387 | struct omapfb_info *ofbi = FB2OFB(fbi); | 1387 | struct omapfb_info *ofbi = FB2OFB(fbi); |
1388 | struct omapfb2_device *fbdev = ofbi->fbdev; | 1388 | struct omapfb2_device *fbdev = ofbi->fbdev; |
1389 | struct omapfb2_mem_region *rg; | 1389 | struct omapfb2_mem_region *rg; |
1390 | void __iomem *vaddr; | 1390 | void __iomem *vaddr; |
1391 | int r; | 1391 | int r; |
1392 | 1392 | ||
1393 | rg = ofbi->region; | 1393 | rg = ofbi->region; |
1394 | 1394 | ||
1395 | rg->paddr = 0; | 1395 | rg->paddr = 0; |
1396 | rg->vaddr = NULL; | 1396 | rg->vaddr = NULL; |
1397 | memset(&rg->vrfb, 0, sizeof rg->vrfb); | 1397 | memset(&rg->vrfb, 0, sizeof rg->vrfb); |
1398 | rg->size = 0; | 1398 | rg->size = 0; |
1399 | rg->type = 0; | 1399 | rg->type = 0; |
1400 | rg->alloc = false; | 1400 | rg->alloc = false; |
1401 | rg->map = false; | 1401 | rg->map = false; |
1402 | 1402 | ||
1403 | size = PAGE_ALIGN(size); | 1403 | size = PAGE_ALIGN(size); |
1404 | 1404 | ||
1405 | if (!paddr) { | 1405 | if (!paddr) { |
1406 | DBG("allocating %lu bytes for fb %d\n", size, ofbi->id); | 1406 | DBG("allocating %lu bytes for fb %d\n", size, ofbi->id); |
1407 | r = omap_vram_alloc(size, &paddr); | 1407 | r = omap_vram_alloc(size, &paddr); |
1408 | } else { | 1408 | } else { |
1409 | DBG("reserving %lu bytes at %lx for fb %d\n", size, paddr, | 1409 | DBG("reserving %lu bytes at %lx for fb %d\n", size, paddr, |
1410 | ofbi->id); | 1410 | ofbi->id); |
1411 | r = omap_vram_reserve(paddr, size); | 1411 | r = omap_vram_reserve(paddr, size); |
1412 | } | 1412 | } |
1413 | 1413 | ||
1414 | if (r) { | 1414 | if (r) { |
1415 | dev_err(fbdev->dev, "failed to allocate framebuffer\n"); | 1415 | dev_err(fbdev->dev, "failed to allocate framebuffer\n"); |
1416 | return -ENOMEM; | 1416 | return -ENOMEM; |
1417 | } | 1417 | } |
1418 | 1418 | ||
1419 | if (ofbi->rotation_type != OMAP_DSS_ROT_VRFB) { | 1419 | if (ofbi->rotation_type != OMAP_DSS_ROT_VRFB) { |
1420 | vaddr = ioremap_wc(paddr, size); | 1420 | vaddr = ioremap_wc(paddr, size); |
1421 | 1421 | ||
1422 | if (!vaddr) { | 1422 | if (!vaddr) { |
1423 | dev_err(fbdev->dev, "failed to ioremap framebuffer\n"); | 1423 | dev_err(fbdev->dev, "failed to ioremap framebuffer\n"); |
1424 | omap_vram_free(paddr, size); | 1424 | omap_vram_free(paddr, size); |
1425 | return -ENOMEM; | 1425 | return -ENOMEM; |
1426 | } | 1426 | } |
1427 | 1427 | ||
1428 | DBG("allocated VRAM paddr %lx, vaddr %p\n", paddr, vaddr); | 1428 | DBG("allocated VRAM paddr %lx, vaddr %p\n", paddr, vaddr); |
1429 | } else { | 1429 | } else { |
1430 | r = omap_vrfb_request_ctx(&rg->vrfb); | 1430 | r = omap_vrfb_request_ctx(&rg->vrfb); |
1431 | if (r) { | 1431 | if (r) { |
1432 | dev_err(fbdev->dev, "vrfb create ctx failed\n"); | 1432 | dev_err(fbdev->dev, "vrfb create ctx failed\n"); |
1433 | return r; | 1433 | return r; |
1434 | } | 1434 | } |
1435 | 1435 | ||
1436 | vaddr = NULL; | 1436 | vaddr = NULL; |
1437 | } | 1437 | } |
1438 | 1438 | ||
1439 | rg->paddr = paddr; | 1439 | rg->paddr = paddr; |
1440 | rg->vaddr = vaddr; | 1440 | rg->vaddr = vaddr; |
1441 | rg->size = size; | 1441 | rg->size = size; |
1442 | rg->alloc = 1; | 1442 | rg->alloc = 1; |
1443 | 1443 | ||
1444 | return 0; | 1444 | return 0; |
1445 | } | 1445 | } |
1446 | 1446 | ||
1447 | /* allocate fbmem using display resolution as reference */ | 1447 | /* allocate fbmem using display resolution as reference */ |
1448 | static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size, | 1448 | static int omapfb_alloc_fbmem_display(struct fb_info *fbi, unsigned long size, |
1449 | unsigned long paddr) | 1449 | unsigned long paddr) |
1450 | { | 1450 | { |
1451 | struct omapfb_info *ofbi = FB2OFB(fbi); | 1451 | struct omapfb_info *ofbi = FB2OFB(fbi); |
1452 | struct omapfb2_device *fbdev = ofbi->fbdev; | 1452 | struct omapfb2_device *fbdev = ofbi->fbdev; |
1453 | struct omap_dss_device *display; | 1453 | struct omap_dss_device *display; |
1454 | int bytespp; | 1454 | int bytespp; |
1455 | 1455 | ||
1456 | display = fb2display(fbi); | 1456 | display = fb2display(fbi); |
1457 | 1457 | ||
1458 | if (!display) | 1458 | if (!display) |
1459 | return 0; | 1459 | return 0; |
1460 | 1460 | ||
1461 | switch (omapfb_get_recommended_bpp(fbdev, display)) { | 1461 | switch (omapfb_get_recommended_bpp(fbdev, display)) { |
1462 | case 16: | 1462 | case 16: |
1463 | bytespp = 2; | 1463 | bytespp = 2; |
1464 | break; | 1464 | break; |
1465 | case 24: | 1465 | case 24: |
1466 | bytespp = 4; | 1466 | bytespp = 4; |
1467 | break; | 1467 | break; |
1468 | default: | 1468 | default: |
1469 | bytespp = 4; | 1469 | bytespp = 4; |
1470 | break; | 1470 | break; |
1471 | } | 1471 | } |
1472 | 1472 | ||
1473 | if (!size) { | 1473 | if (!size) { |
1474 | u16 w, h; | 1474 | u16 w, h; |
1475 | 1475 | ||
1476 | display->driver->get_resolution(display, &w, &h); | 1476 | display->driver->get_resolution(display, &w, &h); |
1477 | 1477 | ||
1478 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { | 1478 | if (ofbi->rotation_type == OMAP_DSS_ROT_VRFB) { |
1479 | size = max(omap_vrfb_min_phys_size(w, h, bytespp), | 1479 | size = max(omap_vrfb_min_phys_size(w, h, bytespp), |
1480 | omap_vrfb_min_phys_size(h, w, bytespp)); | 1480 | omap_vrfb_min_phys_size(h, w, bytespp)); |
1481 | 1481 | ||
1482 | DBG("adjusting fb mem size for VRFB, %u -> %lu\n", | 1482 | DBG("adjusting fb mem size for VRFB, %u -> %lu\n", |
1483 | w * h * bytespp, size); | 1483 | w * h * bytespp, size); |
1484 | } else { | 1484 | } else { |
1485 | size = w * h * bytespp; | 1485 | size = w * h * bytespp; |
1486 | } | 1486 | } |
1487 | } | 1487 | } |
1488 | 1488 | ||
1489 | if (!size) | 1489 | if (!size) |
1490 | return 0; | 1490 | return 0; |
1491 | 1491 | ||
1492 | return omapfb_alloc_fbmem(fbi, size, paddr); | 1492 | return omapfb_alloc_fbmem(fbi, size, paddr); |
1493 | } | 1493 | } |
1494 | 1494 | ||
1495 | static int omapfb_parse_vram_param(const char *param, int max_entries, | 1495 | static int omapfb_parse_vram_param(const char *param, int max_entries, |
1496 | unsigned long *sizes, unsigned long *paddrs) | 1496 | unsigned long *sizes, unsigned long *paddrs) |
1497 | { | 1497 | { |
1498 | int fbnum; | 1498 | int fbnum; |
1499 | unsigned long size; | 1499 | unsigned long size; |
1500 | unsigned long paddr = 0; | 1500 | unsigned long paddr = 0; |
1501 | char *p, *start; | 1501 | char *p, *start; |
1502 | 1502 | ||
1503 | start = (char *)param; | 1503 | start = (char *)param; |
1504 | 1504 | ||
1505 | while (1) { | 1505 | while (1) { |
1506 | p = start; | 1506 | p = start; |
1507 | 1507 | ||
1508 | fbnum = simple_strtoul(p, &p, 10); | 1508 | fbnum = simple_strtoul(p, &p, 10); |
1509 | 1509 | ||
1510 | if (p == start) | 1510 | if (p == start) |
1511 | return -EINVAL; | 1511 | return -EINVAL; |
1512 | 1512 | ||
1513 | if (*p != ':') | 1513 | if (*p != ':') |
1514 | return -EINVAL; | 1514 | return -EINVAL; |
1515 | 1515 | ||
1516 | if (fbnum >= max_entries) | 1516 | if (fbnum >= max_entries) |
1517 | return -EINVAL; | 1517 | return -EINVAL; |
1518 | 1518 | ||
1519 | size = memparse(p + 1, &p); | 1519 | size = memparse(p + 1, &p); |
1520 | 1520 | ||
1521 | if (!size) | 1521 | if (!size) |
1522 | return -EINVAL; | 1522 | return -EINVAL; |
1523 | 1523 | ||
1524 | paddr = 0; | 1524 | paddr = 0; |
1525 | 1525 | ||
1526 | if (*p == '@') { | 1526 | if (*p == '@') { |
1527 | paddr = simple_strtoul(p + 1, &p, 16); | 1527 | paddr = simple_strtoul(p + 1, &p, 16); |
1528 | 1528 | ||
1529 | if (!paddr) | 1529 | if (!paddr) |
1530 | return -EINVAL; | 1530 | return -EINVAL; |
1531 | 1531 | ||
1532 | } | 1532 | } |
1533 | 1533 | ||
1534 | paddrs[fbnum] = paddr; | 1534 | paddrs[fbnum] = paddr; |
1535 | sizes[fbnum] = size; | 1535 | sizes[fbnum] = size; |
1536 | 1536 | ||
1537 | if (*p == 0) | 1537 | if (*p == 0) |
1538 | break; | 1538 | break; |
1539 | 1539 | ||
1540 | if (*p != ',') | 1540 | if (*p != ',') |
1541 | return -EINVAL; | 1541 | return -EINVAL; |
1542 | 1542 | ||
1543 | ++p; | 1543 | ++p; |
1544 | 1544 | ||
1545 | start = p; | 1545 | start = p; |
1546 | } | 1546 | } |
1547 | 1547 | ||
1548 | return 0; | 1548 | return 0; |
1549 | } | 1549 | } |
1550 | 1550 | ||
1551 | static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev) | 1551 | static int omapfb_allocate_all_fbs(struct omapfb2_device *fbdev) |
1552 | { | 1552 | { |
1553 | int i, r; | 1553 | int i, r; |
1554 | unsigned long vram_sizes[10]; | 1554 | unsigned long vram_sizes[10]; |
1555 | unsigned long vram_paddrs[10]; | 1555 | unsigned long vram_paddrs[10]; |
1556 | 1556 | ||
1557 | memset(&vram_sizes, 0, sizeof(vram_sizes)); | 1557 | memset(&vram_sizes, 0, sizeof(vram_sizes)); |
1558 | memset(&vram_paddrs, 0, sizeof(vram_paddrs)); | 1558 | memset(&vram_paddrs, 0, sizeof(vram_paddrs)); |
1559 | 1559 | ||
1560 | if (def_vram && omapfb_parse_vram_param(def_vram, 10, | 1560 | if (def_vram && omapfb_parse_vram_param(def_vram, 10, |
1561 | vram_sizes, vram_paddrs)) { | 1561 | vram_sizes, vram_paddrs)) { |
1562 | dev_err(fbdev->dev, "failed to parse vram parameter\n"); | 1562 | dev_err(fbdev->dev, "failed to parse vram parameter\n"); |
1563 | 1563 | ||
1564 | memset(&vram_sizes, 0, sizeof(vram_sizes)); | 1564 | memset(&vram_sizes, 0, sizeof(vram_sizes)); |
1565 | memset(&vram_paddrs, 0, sizeof(vram_paddrs)); | 1565 | memset(&vram_paddrs, 0, sizeof(vram_paddrs)); |
1566 | } | 1566 | } |
1567 | 1567 | ||
1568 | for (i = 0; i < fbdev->num_fbs; i++) { | 1568 | for (i = 0; i < fbdev->num_fbs; i++) { |
1569 | /* allocate memory automatically only for fb0, or if | 1569 | /* allocate memory automatically only for fb0, or if |
1570 | * excplicitly defined with vram or plat data option */ | 1570 | * excplicitly defined with vram or plat data option */ |
1571 | if (i == 0 || vram_sizes[i] != 0) { | 1571 | if (i == 0 || vram_sizes[i] != 0) { |
1572 | r = omapfb_alloc_fbmem_display(fbdev->fbs[i], | 1572 | r = omapfb_alloc_fbmem_display(fbdev->fbs[i], |
1573 | vram_sizes[i], vram_paddrs[i]); | 1573 | vram_sizes[i], vram_paddrs[i]); |
1574 | 1574 | ||
1575 | if (r) | 1575 | if (r) |
1576 | return r; | 1576 | return r; |
1577 | } | 1577 | } |
1578 | } | 1578 | } |
1579 | 1579 | ||
1580 | for (i = 0; i < fbdev->num_fbs; i++) { | 1580 | for (i = 0; i < fbdev->num_fbs; i++) { |
1581 | struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); | 1581 | struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); |
1582 | struct omapfb2_mem_region *rg; | 1582 | struct omapfb2_mem_region *rg; |
1583 | rg = ofbi->region; | 1583 | rg = ofbi->region; |
1584 | 1584 | ||
1585 | DBG("region%d phys %08x virt %p size=%lu\n", | 1585 | DBG("region%d phys %08x virt %p size=%lu\n", |
1586 | i, | 1586 | i, |
1587 | rg->paddr, | 1587 | rg->paddr, |
1588 | rg->vaddr, | 1588 | rg->vaddr, |
1589 | rg->size); | 1589 | rg->size); |
1590 | } | 1590 | } |
1591 | 1591 | ||
1592 | return 0; | 1592 | return 0; |
1593 | } | 1593 | } |
1594 | 1594 | ||
1595 | int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type) | 1595 | int omapfb_realloc_fbmem(struct fb_info *fbi, unsigned long size, int type) |
1596 | { | 1596 | { |
1597 | struct omapfb_info *ofbi = FB2OFB(fbi); | 1597 | struct omapfb_info *ofbi = FB2OFB(fbi); |
1598 | struct omapfb2_device *fbdev = ofbi->fbdev; | 1598 | struct omapfb2_device *fbdev = ofbi->fbdev; |
1599 | struct omap_dss_device *display = fb2display(fbi); | 1599 | struct omap_dss_device *display = fb2display(fbi); |
1600 | struct omapfb2_mem_region *rg = ofbi->region; | 1600 | struct omapfb2_mem_region *rg = ofbi->region; |
1601 | unsigned long old_size = rg->size; | 1601 | unsigned long old_size = rg->size; |
1602 | unsigned long old_paddr = rg->paddr; | 1602 | unsigned long old_paddr = rg->paddr; |
1603 | int old_type = rg->type; | 1603 | int old_type = rg->type; |
1604 | int r; | 1604 | int r; |
1605 | 1605 | ||
1606 | if (type != OMAPFB_MEMTYPE_SDRAM) | 1606 | if (type != OMAPFB_MEMTYPE_SDRAM) |
1607 | return -EINVAL; | 1607 | return -EINVAL; |
1608 | 1608 | ||
1609 | size = PAGE_ALIGN(size); | 1609 | size = PAGE_ALIGN(size); |
1610 | 1610 | ||
1611 | if (old_size == size && old_type == type) | 1611 | if (old_size == size && old_type == type) |
1612 | return 0; | 1612 | return 0; |
1613 | 1613 | ||
1614 | if (display && display->driver->sync) | 1614 | if (display && display->driver->sync) |
1615 | display->driver->sync(display); | 1615 | display->driver->sync(display); |
1616 | 1616 | ||
1617 | omapfb_free_fbmem(fbi); | 1617 | omapfb_free_fbmem(fbi); |
1618 | 1618 | ||
1619 | if (size == 0) { | 1619 | if (size == 0) { |
1620 | clear_fb_info(fbi); | 1620 | clear_fb_info(fbi); |
1621 | return 0; | 1621 | return 0; |
1622 | } | 1622 | } |
1623 | 1623 | ||
1624 | r = omapfb_alloc_fbmem(fbi, size, 0); | 1624 | r = omapfb_alloc_fbmem(fbi, size, 0); |
1625 | 1625 | ||
1626 | if (r) { | 1626 | if (r) { |
1627 | if (old_size) | 1627 | if (old_size) |
1628 | omapfb_alloc_fbmem(fbi, old_size, old_paddr); | 1628 | omapfb_alloc_fbmem(fbi, old_size, old_paddr); |
1629 | 1629 | ||
1630 | if (rg->size == 0) | 1630 | if (rg->size == 0) |
1631 | clear_fb_info(fbi); | 1631 | clear_fb_info(fbi); |
1632 | 1632 | ||
1633 | return r; | 1633 | return r; |
1634 | } | 1634 | } |
1635 | 1635 | ||
1636 | if (old_size == size) | 1636 | if (old_size == size) |
1637 | return 0; | 1637 | return 0; |
1638 | 1638 | ||
1639 | if (old_size == 0) { | 1639 | if (old_size == 0) { |
1640 | DBG("initializing fb %d\n", ofbi->id); | 1640 | DBG("initializing fb %d\n", ofbi->id); |
1641 | r = omapfb_fb_init(fbdev, fbi); | 1641 | r = omapfb_fb_init(fbdev, fbi); |
1642 | if (r) { | 1642 | if (r) { |
1643 | DBG("omapfb_fb_init failed\n"); | 1643 | DBG("omapfb_fb_init failed\n"); |
1644 | goto err; | 1644 | goto err; |
1645 | } | 1645 | } |
1646 | r = omapfb_apply_changes(fbi, 1); | 1646 | r = omapfb_apply_changes(fbi, 1); |
1647 | if (r) { | 1647 | if (r) { |
1648 | DBG("omapfb_apply_changes failed\n"); | 1648 | DBG("omapfb_apply_changes failed\n"); |
1649 | goto err; | 1649 | goto err; |
1650 | } | 1650 | } |
1651 | } else { | 1651 | } else { |
1652 | struct fb_var_screeninfo new_var; | 1652 | struct fb_var_screeninfo new_var; |
1653 | memcpy(&new_var, &fbi->var, sizeof(new_var)); | 1653 | memcpy(&new_var, &fbi->var, sizeof(new_var)); |
1654 | r = check_fb_var(fbi, &new_var); | 1654 | r = check_fb_var(fbi, &new_var); |
1655 | if (r) | 1655 | if (r) |
1656 | goto err; | 1656 | goto err; |
1657 | memcpy(&fbi->var, &new_var, sizeof(fbi->var)); | 1657 | memcpy(&fbi->var, &new_var, sizeof(fbi->var)); |
1658 | set_fb_fix(fbi); | 1658 | set_fb_fix(fbi); |
1659 | r = setup_vrfb_rotation(fbi); | 1659 | r = setup_vrfb_rotation(fbi); |
1660 | if (r) | 1660 | if (r) |
1661 | goto err; | 1661 | goto err; |
1662 | } | 1662 | } |
1663 | 1663 | ||
1664 | return 0; | 1664 | return 0; |
1665 | err: | 1665 | err: |
1666 | omapfb_free_fbmem(fbi); | 1666 | omapfb_free_fbmem(fbi); |
1667 | clear_fb_info(fbi); | 1667 | clear_fb_info(fbi); |
1668 | return r; | 1668 | return r; |
1669 | } | 1669 | } |
1670 | 1670 | ||
1671 | static void omapfb_auto_update_work(struct work_struct *work) | 1671 | static void omapfb_auto_update_work(struct work_struct *work) |
1672 | { | 1672 | { |
1673 | struct omap_dss_device *dssdev; | 1673 | struct omap_dss_device *dssdev; |
1674 | struct omap_dss_driver *dssdrv; | 1674 | struct omap_dss_driver *dssdrv; |
1675 | struct omapfb_display_data *d; | 1675 | struct omapfb_display_data *d; |
1676 | u16 w, h; | 1676 | u16 w, h; |
1677 | unsigned int freq; | 1677 | unsigned int freq; |
1678 | struct omapfb2_device *fbdev; | 1678 | struct omapfb2_device *fbdev; |
1679 | 1679 | ||
1680 | d = container_of(work, struct omapfb_display_data, | 1680 | d = container_of(work, struct omapfb_display_data, |
1681 | auto_update_work.work); | 1681 | auto_update_work.work); |
1682 | 1682 | ||
1683 | dssdev = d->dssdev; | 1683 | dssdev = d->dssdev; |
1684 | dssdrv = dssdev->driver; | 1684 | dssdrv = dssdev->driver; |
1685 | fbdev = d->fbdev; | 1685 | fbdev = d->fbdev; |
1686 | 1686 | ||
1687 | if (!dssdrv || !dssdrv->update) | 1687 | if (!dssdrv || !dssdrv->update) |
1688 | return; | 1688 | return; |
1689 | 1689 | ||
1690 | if (dssdrv->sync) | 1690 | if (dssdrv->sync) |
1691 | dssdrv->sync(dssdev); | 1691 | dssdrv->sync(dssdev); |
1692 | 1692 | ||
1693 | dssdrv->get_resolution(dssdev, &w, &h); | 1693 | dssdrv->get_resolution(dssdev, &w, &h); |
1694 | dssdrv->update(dssdev, 0, 0, w, h); | 1694 | dssdrv->update(dssdev, 0, 0, w, h); |
1695 | 1695 | ||
1696 | freq = auto_update_freq; | 1696 | freq = auto_update_freq; |
1697 | if (freq == 0) | 1697 | if (freq == 0) |
1698 | freq = 20; | 1698 | freq = 20; |
1699 | queue_delayed_work(fbdev->auto_update_wq, | 1699 | queue_delayed_work(fbdev->auto_update_wq, |
1700 | &d->auto_update_work, HZ / freq); | 1700 | &d->auto_update_work, HZ / freq); |
1701 | } | 1701 | } |
1702 | 1702 | ||
1703 | void omapfb_start_auto_update(struct omapfb2_device *fbdev, | 1703 | void omapfb_start_auto_update(struct omapfb2_device *fbdev, |
1704 | struct omap_dss_device *display) | 1704 | struct omap_dss_device *display) |
1705 | { | 1705 | { |
1706 | struct omapfb_display_data *d; | 1706 | struct omapfb_display_data *d; |
1707 | 1707 | ||
1708 | if (fbdev->auto_update_wq == NULL) { | 1708 | if (fbdev->auto_update_wq == NULL) { |
1709 | struct workqueue_struct *wq; | 1709 | struct workqueue_struct *wq; |
1710 | 1710 | ||
1711 | wq = create_singlethread_workqueue("omapfb_auto_update"); | 1711 | wq = create_singlethread_workqueue("omapfb_auto_update"); |
1712 | 1712 | ||
1713 | if (wq == NULL) { | 1713 | if (wq == NULL) { |
1714 | dev_err(fbdev->dev, "Failed to create workqueue for " | 1714 | dev_err(fbdev->dev, "Failed to create workqueue for " |
1715 | "auto-update\n"); | 1715 | "auto-update\n"); |
1716 | return; | 1716 | return; |
1717 | } | 1717 | } |
1718 | 1718 | ||
1719 | fbdev->auto_update_wq = wq; | 1719 | fbdev->auto_update_wq = wq; |
1720 | } | 1720 | } |
1721 | 1721 | ||
1722 | d = get_display_data(fbdev, display); | 1722 | d = get_display_data(fbdev, display); |
1723 | 1723 | ||
1724 | INIT_DELAYED_WORK(&d->auto_update_work, omapfb_auto_update_work); | 1724 | INIT_DELAYED_WORK(&d->auto_update_work, omapfb_auto_update_work); |
1725 | 1725 | ||
1726 | d->auto_update_work_enabled = true; | 1726 | d->auto_update_work_enabled = true; |
1727 | 1727 | ||
1728 | omapfb_auto_update_work(&d->auto_update_work.work); | 1728 | omapfb_auto_update_work(&d->auto_update_work.work); |
1729 | } | 1729 | } |
1730 | 1730 | ||
1731 | void omapfb_stop_auto_update(struct omapfb2_device *fbdev, | 1731 | void omapfb_stop_auto_update(struct omapfb2_device *fbdev, |
1732 | struct omap_dss_device *display) | 1732 | struct omap_dss_device *display) |
1733 | { | 1733 | { |
1734 | struct omapfb_display_data *d; | 1734 | struct omapfb_display_data *d; |
1735 | 1735 | ||
1736 | d = get_display_data(fbdev, display); | 1736 | d = get_display_data(fbdev, display); |
1737 | 1737 | ||
1738 | cancel_delayed_work_sync(&d->auto_update_work); | 1738 | cancel_delayed_work_sync(&d->auto_update_work); |
1739 | 1739 | ||
1740 | d->auto_update_work_enabled = false; | 1740 | d->auto_update_work_enabled = false; |
1741 | } | 1741 | } |
1742 | 1742 | ||
1743 | /* initialize fb_info, var, fix to something sane based on the display */ | 1743 | /* initialize fb_info, var, fix to something sane based on the display */ |
1744 | static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi) | 1744 | static int omapfb_fb_init(struct omapfb2_device *fbdev, struct fb_info *fbi) |
1745 | { | 1745 | { |
1746 | struct fb_var_screeninfo *var = &fbi->var; | 1746 | struct fb_var_screeninfo *var = &fbi->var; |
1747 | struct omap_dss_device *display = fb2display(fbi); | 1747 | struct omap_dss_device *display = fb2display(fbi); |
1748 | struct omapfb_info *ofbi = FB2OFB(fbi); | 1748 | struct omapfb_info *ofbi = FB2OFB(fbi); |
1749 | int r = 0; | 1749 | int r = 0; |
1750 | 1750 | ||
1751 | fbi->fbops = &omapfb_ops; | 1751 | fbi->fbops = &omapfb_ops; |
1752 | fbi->flags = FBINFO_FLAG_DEFAULT; | 1752 | fbi->flags = FBINFO_FLAG_DEFAULT; |
1753 | fbi->pseudo_palette = fbdev->pseudo_palette; | 1753 | fbi->pseudo_palette = fbdev->pseudo_palette; |
1754 | 1754 | ||
1755 | if (ofbi->region->size == 0) { | 1755 | if (ofbi->region->size == 0) { |
1756 | clear_fb_info(fbi); | 1756 | clear_fb_info(fbi); |
1757 | return 0; | 1757 | return 0; |
1758 | } | 1758 | } |
1759 | 1759 | ||
1760 | var->nonstd = 0; | 1760 | var->nonstd = 0; |
1761 | var->bits_per_pixel = 0; | 1761 | var->bits_per_pixel = 0; |
1762 | 1762 | ||
1763 | var->rotate = def_rotate; | 1763 | var->rotate = def_rotate; |
1764 | 1764 | ||
1765 | if (display) { | 1765 | if (display) { |
1766 | u16 w, h; | 1766 | u16 w, h; |
1767 | int rotation = (var->rotate + ofbi->rotation[0]) % 4; | 1767 | int rotation = (var->rotate + ofbi->rotation[0]) % 4; |
1768 | 1768 | ||
1769 | display->driver->get_resolution(display, &w, &h); | 1769 | display->driver->get_resolution(display, &w, &h); |
1770 | 1770 | ||
1771 | if (rotation == FB_ROTATE_CW || | 1771 | if (rotation == FB_ROTATE_CW || |
1772 | rotation == FB_ROTATE_CCW) { | 1772 | rotation == FB_ROTATE_CCW) { |
1773 | var->xres = h; | 1773 | var->xres = h; |
1774 | var->yres = w; | 1774 | var->yres = w; |
1775 | } else { | 1775 | } else { |
1776 | var->xres = w; | 1776 | var->xres = w; |
1777 | var->yres = h; | 1777 | var->yres = h; |
1778 | } | 1778 | } |
1779 | 1779 | ||
1780 | var->xres_virtual = var->xres; | 1780 | var->xres_virtual = var->xres; |
1781 | var->yres_virtual = var->yres; | 1781 | var->yres_virtual = var->yres; |
1782 | 1782 | ||
1783 | if (!var->bits_per_pixel) { | 1783 | if (!var->bits_per_pixel) { |
1784 | switch (omapfb_get_recommended_bpp(fbdev, display)) { | 1784 | switch (omapfb_get_recommended_bpp(fbdev, display)) { |
1785 | case 16: | 1785 | case 16: |
1786 | var->bits_per_pixel = 16; | 1786 | var->bits_per_pixel = 16; |
1787 | break; | 1787 | break; |
1788 | case 24: | 1788 | case 24: |
1789 | var->bits_per_pixel = 32; | 1789 | var->bits_per_pixel = 32; |
1790 | break; | 1790 | break; |
1791 | default: | 1791 | default: |
1792 | dev_err(fbdev->dev, "illegal display " | 1792 | dev_err(fbdev->dev, "illegal display " |
1793 | "bpp\n"); | 1793 | "bpp\n"); |
1794 | return -EINVAL; | 1794 | return -EINVAL; |
1795 | } | 1795 | } |
1796 | } | 1796 | } |
1797 | } else { | 1797 | } else { |
1798 | /* if there's no display, let's just guess some basic values */ | 1798 | /* if there's no display, let's just guess some basic values */ |
1799 | var->xres = 320; | 1799 | var->xres = 320; |
1800 | var->yres = 240; | 1800 | var->yres = 240; |
1801 | var->xres_virtual = var->xres; | 1801 | var->xres_virtual = var->xres; |
1802 | var->yres_virtual = var->yres; | 1802 | var->yres_virtual = var->yres; |
1803 | if (!var->bits_per_pixel) | 1803 | if (!var->bits_per_pixel) |
1804 | var->bits_per_pixel = 16; | 1804 | var->bits_per_pixel = 16; |
1805 | } | 1805 | } |
1806 | 1806 | ||
1807 | r = check_fb_var(fbi, var); | 1807 | r = check_fb_var(fbi, var); |
1808 | if (r) | 1808 | if (r) |
1809 | goto err; | 1809 | goto err; |
1810 | 1810 | ||
1811 | set_fb_fix(fbi); | 1811 | set_fb_fix(fbi); |
1812 | r = setup_vrfb_rotation(fbi); | 1812 | r = setup_vrfb_rotation(fbi); |
1813 | if (r) | 1813 | if (r) |
1814 | goto err; | 1814 | goto err; |
1815 | 1815 | ||
1816 | r = fb_alloc_cmap(&fbi->cmap, 256, 0); | 1816 | r = fb_alloc_cmap(&fbi->cmap, 256, 0); |
1817 | if (r) | 1817 | if (r) |
1818 | dev_err(fbdev->dev, "unable to allocate color map memory\n"); | 1818 | dev_err(fbdev->dev, "unable to allocate color map memory\n"); |
1819 | 1819 | ||
1820 | err: | 1820 | err: |
1821 | return r; | 1821 | return r; |
1822 | } | 1822 | } |
1823 | 1823 | ||
1824 | static void fbinfo_cleanup(struct omapfb2_device *fbdev, struct fb_info *fbi) | 1824 | static void fbinfo_cleanup(struct omapfb2_device *fbdev, struct fb_info *fbi) |
1825 | { | 1825 | { |
1826 | fb_dealloc_cmap(&fbi->cmap); | 1826 | fb_dealloc_cmap(&fbi->cmap); |
1827 | } | 1827 | } |
1828 | 1828 | ||
1829 | 1829 | ||
1830 | static void omapfb_free_resources(struct omapfb2_device *fbdev) | 1830 | static void omapfb_free_resources(struct omapfb2_device *fbdev) |
1831 | { | 1831 | { |
1832 | int i; | 1832 | int i; |
1833 | 1833 | ||
1834 | DBG("free_resources\n"); | 1834 | DBG("free_resources\n"); |
1835 | 1835 | ||
1836 | if (fbdev == NULL) | 1836 | if (fbdev == NULL) |
1837 | return; | 1837 | return; |
1838 | 1838 | ||
1839 | for (i = 0; i < fbdev->num_fbs; i++) | 1839 | for (i = 0; i < fbdev->num_fbs; i++) |
1840 | unregister_framebuffer(fbdev->fbs[i]); | 1840 | unregister_framebuffer(fbdev->fbs[i]); |
1841 | 1841 | ||
1842 | /* free the reserved fbmem */ | 1842 | /* free the reserved fbmem */ |
1843 | omapfb_free_all_fbmem(fbdev); | 1843 | omapfb_free_all_fbmem(fbdev); |
1844 | 1844 | ||
1845 | for (i = 0; i < fbdev->num_fbs; i++) { | 1845 | for (i = 0; i < fbdev->num_fbs; i++) { |
1846 | fbinfo_cleanup(fbdev, fbdev->fbs[i]); | 1846 | fbinfo_cleanup(fbdev, fbdev->fbs[i]); |
1847 | framebuffer_release(fbdev->fbs[i]); | 1847 | framebuffer_release(fbdev->fbs[i]); |
1848 | } | 1848 | } |
1849 | 1849 | ||
1850 | for (i = 0; i < fbdev->num_displays; i++) { | 1850 | for (i = 0; i < fbdev->num_displays; i++) { |
1851 | struct omap_dss_device *dssdev = fbdev->displays[i].dssdev; | 1851 | struct omap_dss_device *dssdev = fbdev->displays[i].dssdev; |
1852 | 1852 | ||
1853 | if (fbdev->displays[i].auto_update_work_enabled) | 1853 | if (fbdev->displays[i].auto_update_work_enabled) |
1854 | omapfb_stop_auto_update(fbdev, dssdev); | 1854 | omapfb_stop_auto_update(fbdev, dssdev); |
1855 | 1855 | ||
1856 | if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) | 1856 | if (dssdev->state != OMAP_DSS_DISPLAY_DISABLED) |
1857 | dssdev->driver->disable(dssdev); | 1857 | dssdev->driver->disable(dssdev); |
1858 | 1858 | ||
1859 | omap_dss_put_device(dssdev); | 1859 | omap_dss_put_device(dssdev); |
1860 | } | 1860 | } |
1861 | 1861 | ||
1862 | if (fbdev->auto_update_wq != NULL) { | 1862 | if (fbdev->auto_update_wq != NULL) { |
1863 | flush_workqueue(fbdev->auto_update_wq); | 1863 | flush_workqueue(fbdev->auto_update_wq); |
1864 | destroy_workqueue(fbdev->auto_update_wq); | 1864 | destroy_workqueue(fbdev->auto_update_wq); |
1865 | fbdev->auto_update_wq = NULL; | 1865 | fbdev->auto_update_wq = NULL; |
1866 | } | 1866 | } |
1867 | 1867 | ||
1868 | dev_set_drvdata(fbdev->dev, NULL); | 1868 | dev_set_drvdata(fbdev->dev, NULL); |
1869 | kfree(fbdev); | 1869 | kfree(fbdev); |
1870 | } | 1870 | } |
1871 | 1871 | ||
1872 | static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) | 1872 | static int omapfb_create_framebuffers(struct omapfb2_device *fbdev) |
1873 | { | 1873 | { |
1874 | int r, i; | 1874 | int r, i; |
1875 | 1875 | ||
1876 | fbdev->num_fbs = 0; | 1876 | fbdev->num_fbs = 0; |
1877 | 1877 | ||
1878 | DBG("create %d framebuffers\n", CONFIG_FB_OMAP2_NUM_FBS); | 1878 | DBG("create %d framebuffers\n", CONFIG_FB_OMAP2_NUM_FBS); |
1879 | 1879 | ||
1880 | /* allocate fb_infos */ | 1880 | /* allocate fb_infos */ |
1881 | for (i = 0; i < CONFIG_FB_OMAP2_NUM_FBS; i++) { | 1881 | for (i = 0; i < CONFIG_FB_OMAP2_NUM_FBS; i++) { |
1882 | struct fb_info *fbi; | 1882 | struct fb_info *fbi; |
1883 | struct omapfb_info *ofbi; | 1883 | struct omapfb_info *ofbi; |
1884 | 1884 | ||
1885 | fbi = framebuffer_alloc(sizeof(struct omapfb_info), | 1885 | fbi = framebuffer_alloc(sizeof(struct omapfb_info), |
1886 | fbdev->dev); | 1886 | fbdev->dev); |
1887 | 1887 | ||
1888 | if (fbi == NULL) { | 1888 | if (fbi == NULL) { |
1889 | dev_err(fbdev->dev, | 1889 | dev_err(fbdev->dev, |
1890 | "unable to allocate memory for plane info\n"); | 1890 | "unable to allocate memory for plane info\n"); |
1891 | return -ENOMEM; | 1891 | return -ENOMEM; |
1892 | } | 1892 | } |
1893 | 1893 | ||
1894 | clear_fb_info(fbi); | 1894 | clear_fb_info(fbi); |
1895 | 1895 | ||
1896 | fbdev->fbs[i] = fbi; | 1896 | fbdev->fbs[i] = fbi; |
1897 | 1897 | ||
1898 | ofbi = FB2OFB(fbi); | 1898 | ofbi = FB2OFB(fbi); |
1899 | ofbi->fbdev = fbdev; | 1899 | ofbi->fbdev = fbdev; |
1900 | ofbi->id = i; | 1900 | ofbi->id = i; |
1901 | 1901 | ||
1902 | ofbi->region = &fbdev->regions[i]; | 1902 | ofbi->region = &fbdev->regions[i]; |
1903 | ofbi->region->id = i; | 1903 | ofbi->region->id = i; |
1904 | init_rwsem(&ofbi->region->lock); | 1904 | init_rwsem(&ofbi->region->lock); |
1905 | 1905 | ||
1906 | /* assign these early, so that fb alloc can use them */ | 1906 | /* assign these early, so that fb alloc can use them */ |
1907 | ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB : | 1907 | ofbi->rotation_type = def_vrfb ? OMAP_DSS_ROT_VRFB : |
1908 | OMAP_DSS_ROT_DMA; | 1908 | OMAP_DSS_ROT_DMA; |
1909 | ofbi->mirror = def_mirror; | 1909 | ofbi->mirror = def_mirror; |
1910 | 1910 | ||
1911 | fbdev->num_fbs++; | 1911 | fbdev->num_fbs++; |
1912 | } | 1912 | } |
1913 | 1913 | ||
1914 | DBG("fb_infos allocated\n"); | 1914 | DBG("fb_infos allocated\n"); |
1915 | 1915 | ||
1916 | /* assign overlays for the fbs */ | 1916 | /* assign overlays for the fbs */ |
1917 | for (i = 0; i < min(fbdev->num_fbs, fbdev->num_overlays); i++) { | 1917 | for (i = 0; i < min(fbdev->num_fbs, fbdev->num_overlays); i++) { |
1918 | struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); | 1918 | struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[i]); |
1919 | 1919 | ||
1920 | ofbi->overlays[0] = fbdev->overlays[i]; | 1920 | ofbi->overlays[0] = fbdev->overlays[i]; |
1921 | ofbi->num_overlays = 1; | 1921 | ofbi->num_overlays = 1; |
1922 | } | 1922 | } |
1923 | 1923 | ||
1924 | /* allocate fb memories */ | 1924 | /* allocate fb memories */ |
1925 | r = omapfb_allocate_all_fbs(fbdev); | 1925 | r = omapfb_allocate_all_fbs(fbdev); |
1926 | if (r) { | 1926 | if (r) { |
1927 | dev_err(fbdev->dev, "failed to allocate fbmem\n"); | 1927 | dev_err(fbdev->dev, "failed to allocate fbmem\n"); |
1928 | return r; | 1928 | return r; |
1929 | } | 1929 | } |
1930 | 1930 | ||
1931 | DBG("fbmems allocated\n"); | 1931 | DBG("fbmems allocated\n"); |
1932 | 1932 | ||
1933 | /* setup fb_infos */ | 1933 | /* setup fb_infos */ |
1934 | for (i = 0; i < fbdev->num_fbs; i++) { | 1934 | for (i = 0; i < fbdev->num_fbs; i++) { |
1935 | struct fb_info *fbi = fbdev->fbs[i]; | 1935 | struct fb_info *fbi = fbdev->fbs[i]; |
1936 | struct omapfb_info *ofbi = FB2OFB(fbi); | 1936 | struct omapfb_info *ofbi = FB2OFB(fbi); |
1937 | 1937 | ||
1938 | omapfb_get_mem_region(ofbi->region); | 1938 | omapfb_get_mem_region(ofbi->region); |
1939 | r = omapfb_fb_init(fbdev, fbi); | 1939 | r = omapfb_fb_init(fbdev, fbi); |
1940 | omapfb_put_mem_region(ofbi->region); | 1940 | omapfb_put_mem_region(ofbi->region); |
1941 | 1941 | ||
1942 | if (r) { | 1942 | if (r) { |
1943 | dev_err(fbdev->dev, "failed to setup fb_info\n"); | 1943 | dev_err(fbdev->dev, "failed to setup fb_info\n"); |
1944 | return r; | 1944 | return r; |
1945 | } | 1945 | } |
1946 | } | 1946 | } |
1947 | 1947 | ||
1948 | DBG("fb_infos initialized\n"); | 1948 | DBG("fb_infos initialized\n"); |
1949 | 1949 | ||
1950 | for (i = 0; i < fbdev->num_fbs; i++) { | 1950 | for (i = 0; i < fbdev->num_fbs; i++) { |
1951 | r = register_framebuffer(fbdev->fbs[i]); | 1951 | r = register_framebuffer(fbdev->fbs[i]); |
1952 | if (r != 0) { | 1952 | if (r != 0) { |
1953 | dev_err(fbdev->dev, | 1953 | dev_err(fbdev->dev, |
1954 | "registering framebuffer %d failed\n", i); | 1954 | "registering framebuffer %d failed\n", i); |
1955 | return r; | 1955 | return r; |
1956 | } | 1956 | } |
1957 | } | 1957 | } |
1958 | 1958 | ||
1959 | DBG("framebuffers registered\n"); | 1959 | DBG("framebuffers registered\n"); |
1960 | 1960 | ||
1961 | for (i = 0; i < fbdev->num_fbs; i++) { | 1961 | for (i = 0; i < fbdev->num_fbs; i++) { |
1962 | struct fb_info *fbi = fbdev->fbs[i]; | 1962 | struct fb_info *fbi = fbdev->fbs[i]; |
1963 | struct omapfb_info *ofbi = FB2OFB(fbi); | 1963 | struct omapfb_info *ofbi = FB2OFB(fbi); |
1964 | 1964 | ||
1965 | omapfb_get_mem_region(ofbi->region); | 1965 | omapfb_get_mem_region(ofbi->region); |
1966 | r = omapfb_apply_changes(fbi, 1); | 1966 | r = omapfb_apply_changes(fbi, 1); |
1967 | omapfb_put_mem_region(ofbi->region); | 1967 | omapfb_put_mem_region(ofbi->region); |
1968 | 1968 | ||
1969 | if (r) { | 1969 | if (r) { |
1970 | dev_err(fbdev->dev, "failed to change mode\n"); | 1970 | dev_err(fbdev->dev, "failed to change mode\n"); |
1971 | return r; | 1971 | return r; |
1972 | } | 1972 | } |
1973 | } | 1973 | } |
1974 | 1974 | ||
1975 | /* Enable fb0 */ | 1975 | /* Enable fb0 */ |
1976 | if (fbdev->num_fbs > 0) { | 1976 | if (fbdev->num_fbs > 0) { |
1977 | struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[0]); | 1977 | struct omapfb_info *ofbi = FB2OFB(fbdev->fbs[0]); |
1978 | 1978 | ||
1979 | if (ofbi->num_overlays > 0) { | 1979 | if (ofbi->num_overlays > 0) { |
1980 | struct omap_overlay *ovl = ofbi->overlays[0]; | 1980 | struct omap_overlay *ovl = ofbi->overlays[0]; |
1981 | 1981 | ||
1982 | ovl->manager->apply(ovl->manager); | 1982 | ovl->manager->apply(ovl->manager); |
1983 | 1983 | ||
1984 | r = omapfb_overlay_enable(ovl, 1); | 1984 | r = omapfb_overlay_enable(ovl, 1); |
1985 | 1985 | ||
1986 | if (r) { | 1986 | if (r) { |
1987 | dev_err(fbdev->dev, | 1987 | dev_err(fbdev->dev, |
1988 | "failed to enable overlay\n"); | 1988 | "failed to enable overlay\n"); |
1989 | return r; | 1989 | return r; |
1990 | } | 1990 | } |
1991 | } | 1991 | } |
1992 | } | 1992 | } |
1993 | 1993 | ||
1994 | DBG("create_framebuffers done\n"); | 1994 | DBG("create_framebuffers done\n"); |
1995 | 1995 | ||
1996 | return 0; | 1996 | return 0; |
1997 | } | 1997 | } |
1998 | 1998 | ||
1999 | static int omapfb_mode_to_timings(const char *mode_str, | 1999 | static int omapfb_mode_to_timings(const char *mode_str, |
2000 | struct omap_dss_device *display, | 2000 | struct omap_dss_device *display, |
2001 | struct omap_video_timings *timings, u8 *bpp) | 2001 | struct omap_video_timings *timings, u8 *bpp) |
2002 | { | 2002 | { |
2003 | struct fb_info *fbi; | 2003 | struct fb_info *fbi; |
2004 | struct fb_var_screeninfo *var; | 2004 | struct fb_var_screeninfo *var; |
2005 | struct fb_ops *fbops; | 2005 | struct fb_ops *fbops; |
2006 | int r; | 2006 | int r; |
2007 | 2007 | ||
2008 | #ifdef CONFIG_OMAP2_DSS_VENC | 2008 | #ifdef CONFIG_OMAP2_DSS_VENC |
2009 | if (strcmp(mode_str, "pal") == 0) { | 2009 | if (strcmp(mode_str, "pal") == 0) { |
2010 | *timings = omap_dss_pal_timings; | 2010 | *timings = omap_dss_pal_timings; |
2011 | *bpp = 24; | 2011 | *bpp = 24; |
2012 | return 0; | 2012 | return 0; |
2013 | } else if (strcmp(mode_str, "ntsc") == 0) { | 2013 | } else if (strcmp(mode_str, "ntsc") == 0) { |
2014 | *timings = omap_dss_ntsc_timings; | 2014 | *timings = omap_dss_ntsc_timings; |
2015 | *bpp = 24; | 2015 | *bpp = 24; |
2016 | return 0; | 2016 | return 0; |
2017 | } | 2017 | } |
2018 | #endif | 2018 | #endif |
2019 | 2019 | ||
2020 | /* this is quite a hack, but I wanted to use the modedb and for | 2020 | /* this is quite a hack, but I wanted to use the modedb and for |
2021 | * that we need fb_info and var, so we create dummy ones */ | 2021 | * that we need fb_info and var, so we create dummy ones */ |
2022 | 2022 | ||
2023 | *bpp = 0; | 2023 | *bpp = 0; |
2024 | fbi = NULL; | 2024 | fbi = NULL; |
2025 | var = NULL; | 2025 | var = NULL; |
2026 | fbops = NULL; | 2026 | fbops = NULL; |
2027 | 2027 | ||
2028 | fbi = kzalloc(sizeof(*fbi), GFP_KERNEL); | 2028 | fbi = kzalloc(sizeof(*fbi), GFP_KERNEL); |
2029 | if (fbi == NULL) { | 2029 | if (fbi == NULL) { |
2030 | r = -ENOMEM; | 2030 | r = -ENOMEM; |
2031 | goto err; | 2031 | goto err; |
2032 | } | 2032 | } |
2033 | 2033 | ||
2034 | var = kzalloc(sizeof(*var), GFP_KERNEL); | 2034 | var = kzalloc(sizeof(*var), GFP_KERNEL); |
2035 | if (var == NULL) { | 2035 | if (var == NULL) { |
2036 | r = -ENOMEM; | 2036 | r = -ENOMEM; |
2037 | goto err; | 2037 | goto err; |
2038 | } | 2038 | } |
2039 | 2039 | ||
2040 | fbops = kzalloc(sizeof(*fbops), GFP_KERNEL); | 2040 | fbops = kzalloc(sizeof(*fbops), GFP_KERNEL); |
2041 | if (fbops == NULL) { | 2041 | if (fbops == NULL) { |
2042 | r = -ENOMEM; | 2042 | r = -ENOMEM; |
2043 | goto err; | 2043 | goto err; |
2044 | } | 2044 | } |
2045 | 2045 | ||
2046 | fbi->fbops = fbops; | 2046 | fbi->fbops = fbops; |
2047 | 2047 | ||
2048 | r = fb_find_mode(var, fbi, mode_str, NULL, 0, NULL, 24); | 2048 | r = fb_find_mode(var, fbi, mode_str, NULL, 0, NULL, 24); |
2049 | if (r == 0) { | 2049 | if (r == 0) { |
2050 | r = -EINVAL; | 2050 | r = -EINVAL; |
2051 | goto err; | 2051 | goto err; |
2052 | } | 2052 | } |
2053 | 2053 | ||
2054 | if (display->driver->get_timings) { | 2054 | if (display->driver->get_timings) { |
2055 | display->driver->get_timings(display, timings); | 2055 | display->driver->get_timings(display, timings); |
2056 | } else { | 2056 | } else { |
2057 | timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; | 2057 | timings->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; |
2058 | timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH; | 2058 | timings->de_level = OMAPDSS_SIG_ACTIVE_HIGH; |
2059 | timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; | 2059 | timings->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; |
2060 | } | 2060 | } |
2061 | 2061 | ||
2062 | timings->pixel_clock = PICOS2KHZ(var->pixclock); | 2062 | timings->pixel_clock = PICOS2KHZ(var->pixclock); |
2063 | timings->hbp = var->left_margin; | 2063 | timings->hbp = var->left_margin; |
2064 | timings->hfp = var->right_margin; | 2064 | timings->hfp = var->right_margin; |
2065 | timings->vbp = var->upper_margin; | 2065 | timings->vbp = var->upper_margin; |
2066 | timings->vfp = var->lower_margin; | 2066 | timings->vfp = var->lower_margin; |
2067 | timings->hsw = var->hsync_len; | 2067 | timings->hsw = var->hsync_len; |
2068 | timings->vsw = var->vsync_len; | 2068 | timings->vsw = var->vsync_len; |
2069 | timings->x_res = var->xres; | 2069 | timings->x_res = var->xres; |
2070 | timings->y_res = var->yres; | 2070 | timings->y_res = var->yres; |
2071 | timings->hsync_level = var->sync & FB_SYNC_HOR_HIGH_ACT ? | 2071 | timings->hsync_level = var->sync & FB_SYNC_HOR_HIGH_ACT ? |
2072 | OMAPDSS_SIG_ACTIVE_HIGH : | 2072 | OMAPDSS_SIG_ACTIVE_HIGH : |
2073 | OMAPDSS_SIG_ACTIVE_LOW; | 2073 | OMAPDSS_SIG_ACTIVE_LOW; |
2074 | timings->vsync_level = var->sync & FB_SYNC_VERT_HIGH_ACT ? | 2074 | timings->vsync_level = var->sync & FB_SYNC_VERT_HIGH_ACT ? |
2075 | OMAPDSS_SIG_ACTIVE_HIGH : | 2075 | OMAPDSS_SIG_ACTIVE_HIGH : |
2076 | OMAPDSS_SIG_ACTIVE_LOW; | 2076 | OMAPDSS_SIG_ACTIVE_LOW; |
2077 | timings->interlace = var->vmode & FB_VMODE_INTERLACED; | 2077 | timings->interlace = var->vmode & FB_VMODE_INTERLACED; |
2078 | 2078 | ||
2079 | switch (var->bits_per_pixel) { | 2079 | switch (var->bits_per_pixel) { |
2080 | case 16: | 2080 | case 16: |
2081 | *bpp = 16; | 2081 | *bpp = 16; |
2082 | break; | 2082 | break; |
2083 | case 24: | 2083 | case 24: |
2084 | case 32: | 2084 | case 32: |
2085 | default: | 2085 | default: |
2086 | *bpp = 24; | 2086 | *bpp = 24; |
2087 | break; | 2087 | break; |
2088 | } | 2088 | } |
2089 | 2089 | ||
2090 | r = 0; | 2090 | r = 0; |
2091 | 2091 | ||
2092 | err: | 2092 | err: |
2093 | kfree(fbi); | 2093 | kfree(fbi); |
2094 | kfree(var); | 2094 | kfree(var); |
2095 | kfree(fbops); | 2095 | kfree(fbops); |
2096 | 2096 | ||
2097 | return r; | 2097 | return r; |
2098 | } | 2098 | } |
2099 | 2099 | ||
2100 | static int omapfb_set_def_mode(struct omapfb2_device *fbdev, | 2100 | static int omapfb_set_def_mode(struct omapfb2_device *fbdev, |
2101 | struct omap_dss_device *display, char *mode_str) | 2101 | struct omap_dss_device *display, char *mode_str) |
2102 | { | 2102 | { |
2103 | int r; | 2103 | int r; |
2104 | u8 bpp; | 2104 | u8 bpp; |
2105 | struct omap_video_timings timings, temp_timings; | 2105 | struct omap_video_timings timings, temp_timings; |
2106 | struct omapfb_display_data *d; | 2106 | struct omapfb_display_data *d; |
2107 | 2107 | ||
2108 | r = omapfb_mode_to_timings(mode_str, display, &timings, &bpp); | 2108 | r = omapfb_mode_to_timings(mode_str, display, &timings, &bpp); |
2109 | if (r) | 2109 | if (r) |
2110 | return r; | 2110 | return r; |
2111 | 2111 | ||
2112 | d = get_display_data(fbdev, display); | 2112 | d = get_display_data(fbdev, display); |
2113 | d->bpp_override = bpp; | 2113 | d->bpp_override = bpp; |
2114 | 2114 | ||
2115 | if (display->driver->check_timings) { | 2115 | if (display->driver->check_timings) { |
2116 | r = display->driver->check_timings(display, &timings); | 2116 | r = display->driver->check_timings(display, &timings); |
2117 | if (r) | 2117 | if (r) |
2118 | return r; | 2118 | return r; |
2119 | } else { | 2119 | } else { |
2120 | /* If check_timings is not present compare xres and yres */ | 2120 | /* If check_timings is not present compare xres and yres */ |
2121 | if (display->driver->get_timings) { | 2121 | if (display->driver->get_timings) { |
2122 | display->driver->get_timings(display, &temp_timings); | 2122 | display->driver->get_timings(display, &temp_timings); |
2123 | 2123 | ||
2124 | if (temp_timings.x_res != timings.x_res || | 2124 | if (temp_timings.x_res != timings.x_res || |
2125 | temp_timings.y_res != timings.y_res) | 2125 | temp_timings.y_res != timings.y_res) |
2126 | return -EINVAL; | 2126 | return -EINVAL; |
2127 | } | 2127 | } |
2128 | } | 2128 | } |
2129 | 2129 | ||
2130 | if (display->driver->set_timings) | 2130 | if (display->driver->set_timings) |
2131 | display->driver->set_timings(display, &timings); | 2131 | display->driver->set_timings(display, &timings); |
2132 | 2132 | ||
2133 | return 0; | 2133 | return 0; |
2134 | } | 2134 | } |
2135 | 2135 | ||
2136 | static int omapfb_get_recommended_bpp(struct omapfb2_device *fbdev, | 2136 | static int omapfb_get_recommended_bpp(struct omapfb2_device *fbdev, |
2137 | struct omap_dss_device *dssdev) | 2137 | struct omap_dss_device *dssdev) |
2138 | { | 2138 | { |
2139 | struct omapfb_display_data *d; | 2139 | struct omapfb_display_data *d; |
2140 | 2140 | ||
2141 | BUG_ON(dssdev->driver->get_recommended_bpp == NULL); | 2141 | BUG_ON(dssdev->driver->get_recommended_bpp == NULL); |
2142 | 2142 | ||
2143 | d = get_display_data(fbdev, dssdev); | 2143 | d = get_display_data(fbdev, dssdev); |
2144 | 2144 | ||
2145 | if (d->bpp_override != 0) | 2145 | if (d->bpp_override != 0) |
2146 | return d->bpp_override; | 2146 | return d->bpp_override; |
2147 | 2147 | ||
2148 | return dssdev->driver->get_recommended_bpp(dssdev); | 2148 | return dssdev->driver->get_recommended_bpp(dssdev); |
2149 | } | 2149 | } |
2150 | 2150 | ||
2151 | static int omapfb_parse_def_modes(struct omapfb2_device *fbdev) | 2151 | static int omapfb_parse_def_modes(struct omapfb2_device *fbdev) |
2152 | { | 2152 | { |
2153 | char *str, *options, *this_opt; | 2153 | char *str, *options, *this_opt; |
2154 | int r = 0; | 2154 | int r = 0; |
2155 | 2155 | ||
2156 | str = kstrdup(def_mode, GFP_KERNEL); | 2156 | str = kstrdup(def_mode, GFP_KERNEL); |
2157 | if (!str) | 2157 | if (!str) |
2158 | return -ENOMEM; | 2158 | return -ENOMEM; |
2159 | options = str; | 2159 | options = str; |
2160 | 2160 | ||
2161 | while (!r && (this_opt = strsep(&options, ",")) != NULL) { | 2161 | while (!r && (this_opt = strsep(&options, ",")) != NULL) { |
2162 | char *p, *display_str, *mode_str; | 2162 | char *p, *display_str, *mode_str; |
2163 | struct omap_dss_device *display; | 2163 | struct omap_dss_device *display; |
2164 | int i; | 2164 | int i; |
2165 | 2165 | ||
2166 | p = strchr(this_opt, ':'); | 2166 | p = strchr(this_opt, ':'); |
2167 | if (!p) { | 2167 | if (!p) { |
2168 | r = -EINVAL; | 2168 | r = -EINVAL; |
2169 | break; | 2169 | break; |
2170 | } | 2170 | } |
2171 | 2171 | ||
2172 | *p = 0; | 2172 | *p = 0; |
2173 | display_str = this_opt; | 2173 | display_str = this_opt; |
2174 | mode_str = p + 1; | 2174 | mode_str = p + 1; |
2175 | 2175 | ||
2176 | display = NULL; | 2176 | display = NULL; |
2177 | for (i = 0; i < fbdev->num_displays; ++i) { | 2177 | for (i = 0; i < fbdev->num_displays; ++i) { |
2178 | if (strcmp(fbdev->displays[i].dssdev->name, | 2178 | if (strcmp(fbdev->displays[i].dssdev->name, |
2179 | display_str) == 0) { | 2179 | display_str) == 0) { |
2180 | display = fbdev->displays[i].dssdev; | 2180 | display = fbdev->displays[i].dssdev; |
2181 | break; | 2181 | break; |
2182 | } | 2182 | } |
2183 | } | 2183 | } |
2184 | 2184 | ||
2185 | if (!display) { | 2185 | if (!display) { |
2186 | r = -EINVAL; | 2186 | r = -EINVAL; |
2187 | break; | 2187 | break; |
2188 | } | 2188 | } |
2189 | 2189 | ||
2190 | r = omapfb_set_def_mode(fbdev, display, mode_str); | 2190 | r = omapfb_set_def_mode(fbdev, display, mode_str); |
2191 | if (r) | 2191 | if (r) |
2192 | break; | 2192 | break; |
2193 | } | 2193 | } |
2194 | 2194 | ||
2195 | kfree(str); | 2195 | kfree(str); |
2196 | 2196 | ||
2197 | return r; | 2197 | return r; |
2198 | } | 2198 | } |
2199 | 2199 | ||
2200 | static void fb_videomode_to_omap_timings(struct fb_videomode *m, | 2200 | static void fb_videomode_to_omap_timings(struct fb_videomode *m, |
2201 | struct omap_dss_device *display, | 2201 | struct omap_dss_device *display, |
2202 | struct omap_video_timings *t) | 2202 | struct omap_video_timings *t) |
2203 | { | 2203 | { |
2204 | if (display->driver->get_timings) { | 2204 | if (display->driver->get_timings) { |
2205 | display->driver->get_timings(display, t); | 2205 | display->driver->get_timings(display, t); |
2206 | } else { | 2206 | } else { |
2207 | t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; | 2207 | t->data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; |
2208 | t->de_level = OMAPDSS_SIG_ACTIVE_HIGH; | 2208 | t->de_level = OMAPDSS_SIG_ACTIVE_HIGH; |
2209 | t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; | 2209 | t->sync_pclk_edge = OMAPDSS_DRIVE_SIG_OPPOSITE_EDGES; |
2210 | } | 2210 | } |
2211 | 2211 | ||
2212 | t->x_res = m->xres; | 2212 | t->x_res = m->xres; |
2213 | t->y_res = m->yres; | 2213 | t->y_res = m->yres; |
2214 | t->pixel_clock = PICOS2KHZ(m->pixclock); | 2214 | t->pixel_clock = PICOS2KHZ(m->pixclock); |
2215 | t->hsw = m->hsync_len; | 2215 | t->hsw = m->hsync_len; |
2216 | t->hfp = m->right_margin; | 2216 | t->hfp = m->right_margin; |
2217 | t->hbp = m->left_margin; | 2217 | t->hbp = m->left_margin; |
2218 | t->vsw = m->vsync_len; | 2218 | t->vsw = m->vsync_len; |
2219 | t->vfp = m->lower_margin; | 2219 | t->vfp = m->lower_margin; |
2220 | t->vbp = m->upper_margin; | 2220 | t->vbp = m->upper_margin; |
2221 | t->hsync_level = m->sync & FB_SYNC_HOR_HIGH_ACT ? | 2221 | t->hsync_level = m->sync & FB_SYNC_HOR_HIGH_ACT ? |
2222 | OMAPDSS_SIG_ACTIVE_HIGH : | 2222 | OMAPDSS_SIG_ACTIVE_HIGH : |
2223 | OMAPDSS_SIG_ACTIVE_LOW; | 2223 | OMAPDSS_SIG_ACTIVE_LOW; |
2224 | t->vsync_level = m->sync & FB_SYNC_VERT_HIGH_ACT ? | 2224 | t->vsync_level = m->sync & FB_SYNC_VERT_HIGH_ACT ? |
2225 | OMAPDSS_SIG_ACTIVE_HIGH : | 2225 | OMAPDSS_SIG_ACTIVE_HIGH : |
2226 | OMAPDSS_SIG_ACTIVE_LOW; | 2226 | OMAPDSS_SIG_ACTIVE_LOW; |
2227 | t->interlace = m->vmode & FB_VMODE_INTERLACED; | 2227 | t->interlace = m->vmode & FB_VMODE_INTERLACED; |
2228 | } | 2228 | } |
2229 | 2229 | ||
2230 | static int omapfb_find_best_mode(struct omap_dss_device *display, | 2230 | static int omapfb_find_best_mode(struct omap_dss_device *display, |
2231 | struct omap_video_timings *timings) | 2231 | struct omap_video_timings *timings) |
2232 | { | 2232 | { |
2233 | struct fb_monspecs *specs; | 2233 | struct fb_monspecs *specs; |
2234 | u8 *edid; | 2234 | u8 *edid; |
2235 | int r, i, best_xres, best_idx, len; | 2235 | int r, i, best_xres, best_idx, len; |
2236 | 2236 | ||
2237 | if (!display->driver->read_edid) | 2237 | if (!display->driver->read_edid) |
2238 | return -ENODEV; | 2238 | return -ENODEV; |
2239 | 2239 | ||
2240 | len = 0x80 * 2; | 2240 | len = 0x80 * 2; |
2241 | edid = kmalloc(len, GFP_KERNEL); | 2241 | edid = kmalloc(len, GFP_KERNEL); |
2242 | 2242 | ||
2243 | r = display->driver->read_edid(display, edid, len); | 2243 | r = display->driver->read_edid(display, edid, len); |
2244 | if (r < 0) | 2244 | if (r < 0) |
2245 | goto err1; | 2245 | goto err1; |
2246 | 2246 | ||
2247 | specs = kzalloc(sizeof(*specs), GFP_KERNEL); | 2247 | specs = kzalloc(sizeof(*specs), GFP_KERNEL); |
2248 | 2248 | ||
2249 | fb_edid_to_monspecs(edid, specs); | 2249 | fb_edid_to_monspecs(edid, specs); |
2250 | 2250 | ||
2251 | if (edid[126] > 0) | 2251 | if (edid[126] > 0) |
2252 | fb_edid_add_monspecs(edid + 0x80, specs); | 2252 | fb_edid_add_monspecs(edid + 0x80, specs); |
2253 | 2253 | ||
2254 | best_xres = 0; | 2254 | best_xres = 0; |
2255 | best_idx = -1; | 2255 | best_idx = -1; |
2256 | 2256 | ||
2257 | for (i = 0; i < specs->modedb_len; ++i) { | 2257 | for (i = 0; i < specs->modedb_len; ++i) { |
2258 | struct fb_videomode *m; | 2258 | struct fb_videomode *m; |
2259 | struct omap_video_timings t; | 2259 | struct omap_video_timings t; |
2260 | 2260 | ||
2261 | m = &specs->modedb[i]; | 2261 | m = &specs->modedb[i]; |
2262 | 2262 | ||
2263 | if (m->pixclock == 0) | 2263 | if (m->pixclock == 0) |
2264 | continue; | 2264 | continue; |
2265 | 2265 | ||
2266 | /* skip repeated pixel modes */ | 2266 | /* skip repeated pixel modes */ |
2267 | if (m->xres == 2880 || m->xres == 1440) | 2267 | if (m->xres == 2880 || m->xres == 1440) |
2268 | continue; | 2268 | continue; |
2269 | 2269 | ||
2270 | fb_videomode_to_omap_timings(m, display, &t); | 2270 | fb_videomode_to_omap_timings(m, display, &t); |
2271 | 2271 | ||
2272 | r = display->driver->check_timings(display, &t); | 2272 | r = display->driver->check_timings(display, &t); |
2273 | if (r == 0 && best_xres < m->xres) { | 2273 | if (r == 0 && best_xres < m->xres) { |
2274 | best_xres = m->xres; | 2274 | best_xres = m->xres; |
2275 | best_idx = i; | 2275 | best_idx = i; |
2276 | } | 2276 | } |
2277 | } | 2277 | } |
2278 | 2278 | ||
2279 | if (best_xres == 0) { | 2279 | if (best_xres == 0) { |
2280 | r = -ENOENT; | 2280 | r = -ENOENT; |
2281 | goto err2; | 2281 | goto err2; |
2282 | } | 2282 | } |
2283 | 2283 | ||
2284 | fb_videomode_to_omap_timings(&specs->modedb[best_idx], display, | 2284 | fb_videomode_to_omap_timings(&specs->modedb[best_idx], display, |
2285 | timings); | 2285 | timings); |
2286 | 2286 | ||
2287 | r = 0; | 2287 | r = 0; |
2288 | 2288 | ||
2289 | err2: | 2289 | err2: |
2290 | fb_destroy_modedb(specs->modedb); | 2290 | fb_destroy_modedb(specs->modedb); |
2291 | kfree(specs); | 2291 | kfree(specs); |
2292 | err1: | 2292 | err1: |
2293 | kfree(edid); | 2293 | kfree(edid); |
2294 | 2294 | ||
2295 | return r; | 2295 | return r; |
2296 | } | 2296 | } |
2297 | 2297 | ||
2298 | static int omapfb_init_display(struct omapfb2_device *fbdev, | 2298 | static int omapfb_init_display(struct omapfb2_device *fbdev, |
2299 | struct omap_dss_device *dssdev) | 2299 | struct omap_dss_device *dssdev) |
2300 | { | 2300 | { |
2301 | struct omap_dss_driver *dssdrv = dssdev->driver; | 2301 | struct omap_dss_driver *dssdrv = dssdev->driver; |
2302 | struct omapfb_display_data *d; | 2302 | struct omapfb_display_data *d; |
2303 | int r; | 2303 | int r; |
2304 | 2304 | ||
2305 | r = dssdrv->enable(dssdev); | 2305 | r = dssdrv->enable(dssdev); |
2306 | if (r) { | 2306 | if (r) { |
2307 | dev_warn(fbdev->dev, "Failed to enable display '%s'\n", | 2307 | dev_warn(fbdev->dev, "Failed to enable display '%s'\n", |
2308 | dssdev->name); | 2308 | dssdev->name); |
2309 | return r; | 2309 | return r; |
2310 | } | 2310 | } |
2311 | 2311 | ||
2312 | d = get_display_data(fbdev, dssdev); | 2312 | d = get_display_data(fbdev, dssdev); |
2313 | 2313 | ||
2314 | d->fbdev = fbdev; | 2314 | d->fbdev = fbdev; |
2315 | 2315 | ||
2316 | if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { | 2316 | if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) { |
2317 | u16 w, h; | 2317 | u16 w, h; |
2318 | 2318 | ||
2319 | if (auto_update) { | 2319 | if (auto_update) { |
2320 | omapfb_start_auto_update(fbdev, dssdev); | 2320 | omapfb_start_auto_update(fbdev, dssdev); |
2321 | d->update_mode = OMAPFB_AUTO_UPDATE; | 2321 | d->update_mode = OMAPFB_AUTO_UPDATE; |
2322 | } else { | 2322 | } else { |
2323 | d->update_mode = OMAPFB_MANUAL_UPDATE; | 2323 | d->update_mode = OMAPFB_MANUAL_UPDATE; |
2324 | } | 2324 | } |
2325 | 2325 | ||
2326 | if (dssdrv->enable_te) { | 2326 | if (dssdrv->enable_te) { |
2327 | r = dssdrv->enable_te(dssdev, 1); | 2327 | r = dssdrv->enable_te(dssdev, 1); |
2328 | if (r) { | 2328 | if (r) { |
2329 | dev_err(fbdev->dev, "Failed to set TE\n"); | 2329 | dev_err(fbdev->dev, "Failed to set TE\n"); |
2330 | return r; | 2330 | return r; |
2331 | } | 2331 | } |
2332 | } | 2332 | } |
2333 | 2333 | ||
2334 | dssdrv->get_resolution(dssdev, &w, &h); | 2334 | dssdrv->get_resolution(dssdev, &w, &h); |
2335 | r = dssdrv->update(dssdev, 0, 0, w, h); | 2335 | r = dssdrv->update(dssdev, 0, 0, w, h); |
2336 | if (r) { | 2336 | if (r) { |
2337 | dev_err(fbdev->dev, | 2337 | dev_err(fbdev->dev, |
2338 | "Failed to update display\n"); | 2338 | "Failed to update display\n"); |
2339 | return r; | 2339 | return r; |
2340 | } | 2340 | } |
2341 | } else { | 2341 | } else { |
2342 | d->update_mode = OMAPFB_AUTO_UPDATE; | 2342 | d->update_mode = OMAPFB_AUTO_UPDATE; |
2343 | } | 2343 | } |
2344 | 2344 | ||
2345 | return 0; | 2345 | return 0; |
2346 | } | 2346 | } |
2347 | 2347 | ||
2348 | static int __init omapfb_probe(struct platform_device *pdev) | 2348 | static int __init omapfb_probe(struct platform_device *pdev) |
2349 | { | 2349 | { |
2350 | struct omapfb2_device *fbdev = NULL; | 2350 | struct omapfb2_device *fbdev = NULL; |
2351 | int r = 0; | 2351 | int r = 0; |
2352 | int i; | 2352 | int i; |
2353 | struct omap_overlay *ovl; | 2353 | struct omap_overlay *ovl; |
2354 | struct omap_dss_device *def_display; | 2354 | struct omap_dss_device *def_display; |
2355 | struct omap_dss_device *dssdev; | 2355 | struct omap_dss_device *dssdev; |
2356 | 2356 | ||
2357 | DBG("omapfb_probe\n"); | 2357 | DBG("omapfb_probe\n"); |
2358 | 2358 | ||
2359 | if (pdev->num_resources != 0) { | 2359 | if (pdev->num_resources != 0) { |
2360 | dev_err(&pdev->dev, "probed for an unknown device\n"); | 2360 | dev_err(&pdev->dev, "probed for an unknown device\n"); |
2361 | r = -ENODEV; | 2361 | r = -ENODEV; |
2362 | goto err0; | 2362 | goto err0; |
2363 | } | 2363 | } |
2364 | 2364 | ||
2365 | fbdev = kzalloc(sizeof(struct omapfb2_device), GFP_KERNEL); | 2365 | fbdev = kzalloc(sizeof(struct omapfb2_device), GFP_KERNEL); |
2366 | if (fbdev == NULL) { | 2366 | if (fbdev == NULL) { |
2367 | r = -ENOMEM; | 2367 | r = -ENOMEM; |
2368 | goto err0; | 2368 | goto err0; |
2369 | } | 2369 | } |
2370 | 2370 | ||
2371 | /* TODO : Replace cpu check with omap_has_vrfb once HAS_FEATURE | 2371 | /* TODO : Replace cpu check with omap_has_vrfb once HAS_FEATURE |
2372 | * available for OMAP2 and OMAP3 | 2372 | * available for OMAP2 and OMAP3 |
2373 | */ | 2373 | */ |
2374 | if (def_vrfb && !cpu_is_omap24xx() && !cpu_is_omap34xx()) { | 2374 | if (def_vrfb && !cpu_is_omap24xx() && !cpu_is_omap34xx()) { |
2375 | def_vrfb = 0; | 2375 | def_vrfb = 0; |
2376 | dev_warn(&pdev->dev, "VRFB is not supported on this hardware, " | 2376 | dev_warn(&pdev->dev, "VRFB is not supported on this hardware, " |
2377 | "ignoring the module parameter vrfb=y\n"); | 2377 | "ignoring the module parameter vrfb=y\n"); |
2378 | } | 2378 | } |
2379 | 2379 | ||
2380 | 2380 | ||
2381 | mutex_init(&fbdev->mtx); | 2381 | mutex_init(&fbdev->mtx); |
2382 | 2382 | ||
2383 | fbdev->dev = &pdev->dev; | 2383 | fbdev->dev = &pdev->dev; |
2384 | platform_set_drvdata(pdev, fbdev); | 2384 | platform_set_drvdata(pdev, fbdev); |
2385 | 2385 | ||
2386 | r = 0; | 2386 | r = 0; |
2387 | fbdev->num_displays = 0; | 2387 | fbdev->num_displays = 0; |
2388 | dssdev = NULL; | 2388 | dssdev = NULL; |
2389 | for_each_dss_dev(dssdev) { | 2389 | for_each_dss_dev(dssdev) { |
2390 | struct omapfb_display_data *d; | 2390 | struct omapfb_display_data *d; |
2391 | 2391 | ||
2392 | omap_dss_get_device(dssdev); | 2392 | omap_dss_get_device(dssdev); |
2393 | 2393 | ||
2394 | if (!dssdev->driver) { | 2394 | if (!dssdev->driver) { |
2395 | dev_warn(&pdev->dev, "no driver for display: %s\n", | 2395 | dev_warn(&pdev->dev, "no driver for display: %s\n", |
2396 | dssdev->name); | 2396 | dssdev->name); |
2397 | omap_dss_put_device(dssdev); | 2397 | omap_dss_put_device(dssdev); |
2398 | continue; | 2398 | continue; |
2399 | } | 2399 | } |
2400 | 2400 | ||
2401 | d = &fbdev->displays[fbdev->num_displays++]; | 2401 | d = &fbdev->displays[fbdev->num_displays++]; |
2402 | d->dssdev = dssdev; | 2402 | d->dssdev = dssdev; |
2403 | if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) | 2403 | if (dssdev->caps & OMAP_DSS_DISPLAY_CAP_MANUAL_UPDATE) |
2404 | d->update_mode = OMAPFB_MANUAL_UPDATE; | 2404 | d->update_mode = OMAPFB_MANUAL_UPDATE; |
2405 | else | 2405 | else |
2406 | d->update_mode = OMAPFB_AUTO_UPDATE; | 2406 | d->update_mode = OMAPFB_AUTO_UPDATE; |
2407 | } | 2407 | } |
2408 | 2408 | ||
2409 | if (r) | 2409 | if (r) |
2410 | goto cleanup; | 2410 | goto cleanup; |
2411 | 2411 | ||
2412 | if (fbdev->num_displays == 0) { | 2412 | if (fbdev->num_displays == 0) { |
2413 | dev_err(&pdev->dev, "no displays\n"); | 2413 | dev_err(&pdev->dev, "no displays\n"); |
2414 | r = -EINVAL; | 2414 | r = -EINVAL; |
2415 | goto cleanup; | 2415 | goto cleanup; |
2416 | } | 2416 | } |
2417 | 2417 | ||
2418 | fbdev->num_overlays = omap_dss_get_num_overlays(); | 2418 | fbdev->num_overlays = omap_dss_get_num_overlays(); |
2419 | for (i = 0; i < fbdev->num_overlays; i++) | 2419 | for (i = 0; i < fbdev->num_overlays; i++) |
2420 | fbdev->overlays[i] = omap_dss_get_overlay(i); | 2420 | fbdev->overlays[i] = omap_dss_get_overlay(i); |
2421 | 2421 | ||
2422 | fbdev->num_managers = omap_dss_get_num_overlay_managers(); | 2422 | fbdev->num_managers = omap_dss_get_num_overlay_managers(); |
2423 | for (i = 0; i < fbdev->num_managers; i++) | 2423 | for (i = 0; i < fbdev->num_managers; i++) |
2424 | fbdev->managers[i] = omap_dss_get_overlay_manager(i); | 2424 | fbdev->managers[i] = omap_dss_get_overlay_manager(i); |
2425 | 2425 | ||
2426 | /* gfx overlay should be the default one. find a display | 2426 | /* gfx overlay should be the default one. find a display |
2427 | * connected to that, and use it as default display */ | 2427 | * connected to that, and use it as default display */ |
2428 | ovl = omap_dss_get_overlay(0); | 2428 | ovl = omap_dss_get_overlay(0); |
2429 | if (ovl->manager && ovl->manager->device) { | 2429 | if (ovl->manager && ovl->manager->device) { |
2430 | def_display = ovl->manager->device; | 2430 | def_display = ovl->manager->device; |
2431 | } else { | 2431 | } else { |
2432 | dev_warn(&pdev->dev, "cannot find default display\n"); | 2432 | dev_warn(&pdev->dev, "cannot find default display\n"); |
2433 | def_display = NULL; | 2433 | def_display = NULL; |
2434 | } | 2434 | } |
2435 | 2435 | ||
2436 | if (def_mode && strlen(def_mode) > 0) { | 2436 | if (def_mode && strlen(def_mode) > 0) { |
2437 | if (omapfb_parse_def_modes(fbdev)) | 2437 | if (omapfb_parse_def_modes(fbdev)) |
2438 | dev_warn(&pdev->dev, "cannot parse default modes\n"); | 2438 | dev_warn(&pdev->dev, "cannot parse default modes\n"); |
2439 | } else if (def_display && def_display->driver->set_timings && | 2439 | } else if (def_display && def_display->driver->set_timings && |
2440 | def_display->driver->check_timings) { | 2440 | def_display->driver->check_timings) { |
2441 | struct omap_video_timings t; | 2441 | struct omap_video_timings t; |
2442 | 2442 | ||
2443 | r = omapfb_find_best_mode(def_display, &t); | 2443 | r = omapfb_find_best_mode(def_display, &t); |
2444 | 2444 | ||
2445 | if (r == 0) | 2445 | if (r == 0) |
2446 | def_display->driver->set_timings(def_display, &t); | 2446 | def_display->driver->set_timings(def_display, &t); |
2447 | } | 2447 | } |
2448 | 2448 | ||
2449 | r = omapfb_create_framebuffers(fbdev); | 2449 | r = omapfb_create_framebuffers(fbdev); |
2450 | if (r) | 2450 | if (r) |
2451 | goto cleanup; | 2451 | goto cleanup; |
2452 | 2452 | ||
2453 | for (i = 0; i < fbdev->num_managers; i++) { | 2453 | for (i = 0; i < fbdev->num_managers; i++) { |
2454 | struct omap_overlay_manager *mgr; | 2454 | struct omap_overlay_manager *mgr; |
2455 | mgr = fbdev->managers[i]; | 2455 | mgr = fbdev->managers[i]; |
2456 | r = mgr->apply(mgr); | 2456 | r = mgr->apply(mgr); |
2457 | if (r) | 2457 | if (r) |
2458 | dev_warn(fbdev->dev, "failed to apply dispc config\n"); | 2458 | dev_warn(fbdev->dev, "failed to apply dispc config\n"); |
2459 | } | 2459 | } |
2460 | 2460 | ||
2461 | DBG("mgr->apply'ed\n"); | 2461 | DBG("mgr->apply'ed\n"); |
2462 | 2462 | ||
2463 | if (def_display) { | 2463 | if (def_display) { |
2464 | r = omapfb_init_display(fbdev, def_display); | 2464 | r = omapfb_init_display(fbdev, def_display); |
2465 | if (r) { | 2465 | if (r) { |
2466 | dev_err(fbdev->dev, | 2466 | dev_err(fbdev->dev, |
2467 | "failed to initialize default " | 2467 | "failed to initialize default " |
2468 | "display\n"); | 2468 | "display\n"); |
2469 | goto cleanup; | 2469 | goto cleanup; |
2470 | } | 2470 | } |
2471 | } | 2471 | } |
2472 | 2472 | ||
2473 | DBG("create sysfs for fbs\n"); | 2473 | DBG("create sysfs for fbs\n"); |
2474 | r = omapfb_create_sysfs(fbdev); | 2474 | r = omapfb_create_sysfs(fbdev); |
2475 | if (r) { | 2475 | if (r) { |
2476 | dev_err(fbdev->dev, "failed to create sysfs entries\n"); | 2476 | dev_err(fbdev->dev, "failed to create sysfs entries\n"); |
2477 | goto cleanup; | 2477 | goto cleanup; |
2478 | } | 2478 | } |
2479 | 2479 | ||
2480 | return 0; | 2480 | return 0; |
2481 | 2481 | ||
2482 | cleanup: | 2482 | cleanup: |
2483 | omapfb_free_resources(fbdev); | 2483 | omapfb_free_resources(fbdev); |
2484 | err0: | 2484 | err0: |
2485 | dev_err(&pdev->dev, "failed to setup omapfb\n"); | 2485 | dev_err(&pdev->dev, "failed to setup omapfb\n"); |
2486 | return r; | 2486 | return r; |
2487 | } | 2487 | } |
2488 | 2488 | ||
2489 | static int __exit omapfb_remove(struct platform_device *pdev) | 2489 | static int __exit omapfb_remove(struct platform_device *pdev) |
2490 | { | 2490 | { |
2491 | struct omapfb2_device *fbdev = platform_get_drvdata(pdev); | 2491 | struct omapfb2_device *fbdev = platform_get_drvdata(pdev); |
2492 | 2492 | ||
2493 | /* FIXME: wait till completion of pending events */ | 2493 | /* FIXME: wait till completion of pending events */ |
2494 | 2494 | ||
2495 | omapfb_remove_sysfs(fbdev); | 2495 | omapfb_remove_sysfs(fbdev); |
2496 | 2496 | ||
2497 | omapfb_free_resources(fbdev); | 2497 | omapfb_free_resources(fbdev); |
2498 | 2498 | ||
2499 | return 0; | 2499 | return 0; |
2500 | } | 2500 | } |
2501 | 2501 | ||
2502 | static struct platform_driver omapfb_driver = { | 2502 | static struct platform_driver omapfb_driver = { |
2503 | .remove = __exit_p(omapfb_remove), | 2503 | .remove = __exit_p(omapfb_remove), |
2504 | .driver = { | 2504 | .driver = { |
2505 | .name = "omapfb", | 2505 | .name = "omapfb", |
2506 | .owner = THIS_MODULE, | 2506 | .owner = THIS_MODULE, |
2507 | }, | 2507 | }, |
2508 | }; | 2508 | }; |
2509 | 2509 | ||
2510 | static int __init omapfb_init(void) | 2510 | static int __init omapfb_init(void) |
2511 | { | 2511 | { |
2512 | DBG("omapfb_init\n"); | 2512 | DBG("omapfb_init\n"); |
2513 | 2513 | ||
2514 | if (platform_driver_probe(&omapfb_driver, omapfb_probe)) { | 2514 | if (platform_driver_probe(&omapfb_driver, omapfb_probe)) { |
2515 | printk(KERN_ERR "failed to register omapfb driver\n"); | 2515 | printk(KERN_ERR "failed to register omapfb driver\n"); |
2516 | return -ENODEV; | 2516 | return -ENODEV; |
2517 | } | 2517 | } |
2518 | 2518 | ||
2519 | return 0; | 2519 | return 0; |
2520 | } | 2520 | } |
2521 | 2521 | ||
2522 | static void __exit omapfb_exit(void) | 2522 | static void __exit omapfb_exit(void) |
2523 | { | 2523 | { |
2524 | DBG("omapfb_exit\n"); | 2524 | DBG("omapfb_exit\n"); |
2525 | platform_driver_unregister(&omapfb_driver); | 2525 | platform_driver_unregister(&omapfb_driver); |
2526 | } | 2526 | } |
2527 | 2527 | ||
2528 | module_param_named(mode, def_mode, charp, 0); | 2528 | module_param_named(mode, def_mode, charp, 0); |
2529 | module_param_named(vram, def_vram, charp, 0); | 2529 | module_param_named(vram, def_vram, charp, 0); |
2530 | module_param_named(rotate, def_rotate, int, 0); | 2530 | module_param_named(rotate, def_rotate, int, 0); |
2531 | module_param_named(vrfb, def_vrfb, bool, 0); | 2531 | module_param_named(vrfb, def_vrfb, bool, 0); |
2532 | module_param_named(mirror, def_mirror, bool, 0); | 2532 | module_param_named(mirror, def_mirror, bool, 0); |
2533 | 2533 | ||
2534 | /* late_initcall to let panel/ctrl drivers loaded first. | 2534 | /* late_initcall to let panel/ctrl drivers loaded first. |
2535 | * I guess better option would be a more dynamic approach, | 2535 | * I guess better option would be a more dynamic approach, |
2536 | * so that omapfb reacts to new panels when they are loaded */ | 2536 | * so that omapfb reacts to new panels when they are loaded */ |
2537 | late_initcall(omapfb_init); | 2537 | late_initcall(omapfb_init); |
2538 | /*module_init(omapfb_init);*/ | 2538 | /*module_init(omapfb_init);*/ |
2539 | module_exit(omapfb_exit); | 2539 | module_exit(omapfb_exit); |
2540 | 2540 | ||
2541 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); | 2541 | MODULE_AUTHOR("Tomi Valkeinen <tomi.valkeinen@nokia.com>"); |
2542 | MODULE_DESCRIPTION("OMAP2/3 Framebuffer"); | 2542 | MODULE_DESCRIPTION("OMAP2/3 Framebuffer"); |
2543 | MODULE_LICENSE("GPL v2"); | 2543 | MODULE_LICENSE("GPL v2"); |
2544 | 2544 |