Commit | Line | Data |
---|---|---|
5c18adb3 TV |
1 | /* |
2 | * linux/drivers/video/omap2/dss/rfbi.c | |
3 | * | |
4 | * Copyright (C) 2009 Nokia Corporation | |
5 | * Author: Tomi Valkeinen <tomi.valkeinen@nokia.com> | |
6 | * | |
7 | * Some code and ideas taken from drivers/video/omap/ driver | |
8 | * by Imre Deak. | |
9 | * | |
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 | |
12 | * the Free Software Foundation. | |
13 | * | |
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 | |
16 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
17 | * more details. | |
18 | * | |
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/>. | |
21 | */ | |
22 | ||
23 | #define DSS_SUBSYS_NAME "RFBI" | |
24 | ||
25 | #include <linux/kernel.h> | |
26 | #include <linux/dma-mapping.h> | |
a8a35931 | 27 | #include <linux/export.h> |
5c18adb3 TV |
28 | #include <linux/vmalloc.h> |
29 | #include <linux/clk.h> | |
30 | #include <linux/io.h> | |
31 | #include <linux/delay.h> | |
32 | #include <linux/kfifo.h> | |
33 | #include <linux/ktime.h> | |
34 | #include <linux/hrtimer.h> | |
35 | #include <linux/seq_file.h> | |
773139f1 | 36 | #include <linux/semaphore.h> |
24e6289c | 37 | #include <linux/platform_device.h> |
4fbafaf3 | 38 | #include <linux/pm_runtime.h> |
736e60dd | 39 | #include <linux/component.h> |
5c18adb3 | 40 | |
a0b38cc4 | 41 | #include <video/omapdss.h> |
5c18adb3 TV |
42 | #include "dss.h" |
43 | ||
5c18adb3 TV |
44 | struct rfbi_reg { u16 idx; }; |
45 | ||
46 | #define RFBI_REG(idx) ((const struct rfbi_reg) { idx }) | |
47 | ||
48 | #define RFBI_REVISION RFBI_REG(0x0000) | |
49 | #define RFBI_SYSCONFIG RFBI_REG(0x0010) | |
50 | #define RFBI_SYSSTATUS RFBI_REG(0x0014) | |
51 | #define RFBI_CONTROL RFBI_REG(0x0040) | |
52 | #define RFBI_PIXEL_CNT RFBI_REG(0x0044) | |
53 | #define RFBI_LINE_NUMBER RFBI_REG(0x0048) | |
54 | #define RFBI_CMD RFBI_REG(0x004c) | |
55 | #define RFBI_PARAM RFBI_REG(0x0050) | |
56 | #define RFBI_DATA RFBI_REG(0x0054) | |
57 | #define RFBI_READ RFBI_REG(0x0058) | |
58 | #define RFBI_STATUS RFBI_REG(0x005c) | |
59 | ||
60 | #define RFBI_CONFIG(n) RFBI_REG(0x0060 + (n)*0x18) | |
61 | #define RFBI_ONOFF_TIME(n) RFBI_REG(0x0064 + (n)*0x18) | |
62 | #define RFBI_CYCLE_TIME(n) RFBI_REG(0x0068 + (n)*0x18) | |
63 | #define RFBI_DATA_CYCLE1(n) RFBI_REG(0x006c + (n)*0x18) | |
64 | #define RFBI_DATA_CYCLE2(n) RFBI_REG(0x0070 + (n)*0x18) | |
65 | #define RFBI_DATA_CYCLE3(n) RFBI_REG(0x0074 + (n)*0x18) | |
66 | ||
67 | #define RFBI_VSYNC_WIDTH RFBI_REG(0x0090) | |
68 | #define RFBI_HSYNC_WIDTH RFBI_REG(0x0094) | |
69 | ||
5c18adb3 TV |
70 | #define REG_FLD_MOD(idx, val, start, end) \ |
71 | rfbi_write_reg(idx, FLD_MOD(rfbi_read_reg(idx), val, start, end)) | |
72 | ||
5c18adb3 TV |
73 | enum omap_rfbi_cycleformat { |
74 | OMAP_DSS_RFBI_CYCLEFORMAT_1_1 = 0, | |
75 | OMAP_DSS_RFBI_CYCLEFORMAT_2_1 = 1, | |
76 | OMAP_DSS_RFBI_CYCLEFORMAT_3_1 = 2, | |
77 | OMAP_DSS_RFBI_CYCLEFORMAT_3_2 = 3, | |
78 | }; | |
79 | ||
80 | enum omap_rfbi_datatype { | |
81 | OMAP_DSS_RFBI_DATATYPE_12 = 0, | |
82 | OMAP_DSS_RFBI_DATATYPE_16 = 1, | |
83 | OMAP_DSS_RFBI_DATATYPE_18 = 2, | |
84 | OMAP_DSS_RFBI_DATATYPE_24 = 3, | |
85 | }; | |
86 | ||
87 | enum omap_rfbi_parallelmode { | |
88 | OMAP_DSS_RFBI_PARALLELMODE_8 = 0, | |
89 | OMAP_DSS_RFBI_PARALLELMODE_9 = 1, | |
90 | OMAP_DSS_RFBI_PARALLELMODE_12 = 2, | |
91 | OMAP_DSS_RFBI_PARALLELMODE_16 = 3, | |
92 | }; | |
93 | ||
5c18adb3 TV |
94 | static int rfbi_convert_timings(struct rfbi_timings *t); |
95 | static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div); | |
5c18adb3 TV |
96 | |
97 | static struct { | |
3448d500 | 98 | struct platform_device *pdev; |
5c18adb3 TV |
99 | void __iomem *base; |
100 | ||
101 | unsigned long l4_khz; | |
102 | ||
103 | enum omap_rfbi_datatype datatype; | |
104 | enum omap_rfbi_parallelmode parallelmode; | |
105 | ||
106 | enum omap_rfbi_te_mode te_mode; | |
107 | int te_enabled; | |
108 | ||
109 | void (*framedone_callback)(void *data); | |
110 | void *framedone_callback_data; | |
111 | ||
112 | struct omap_dss_device *dssdev[2]; | |
113 | ||
773139f1 | 114 | struct semaphore bus_lock; |
6ff9dd5a AT |
115 | |
116 | struct omap_video_timings timings; | |
b02875be | 117 | int pixel_size; |
475989b7 | 118 | int data_lines; |
6e883324 | 119 | struct rfbi_timings intf_timings; |
81b87f51 | 120 | |
1f68d9c4 | 121 | struct omap_dss_device output; |
5c18adb3 TV |
122 | } rfbi; |
123 | ||
5c18adb3 TV |
124 | static inline void rfbi_write_reg(const struct rfbi_reg idx, u32 val) |
125 | { | |
126 | __raw_writel(val, rfbi.base + idx.idx); | |
127 | } | |
128 | ||
129 | static inline u32 rfbi_read_reg(const struct rfbi_reg idx) | |
130 | { | |
131 | return __raw_readl(rfbi.base + idx.idx); | |
132 | } | |
133 | ||
4fbafaf3 | 134 | static int rfbi_runtime_get(void) |
5c18adb3 | 135 | { |
4fbafaf3 TV |
136 | int r; |
137 | ||
138 | DSSDBG("rfbi_runtime_get\n"); | |
139 | ||
140 | r = pm_runtime_get_sync(&rfbi.pdev->dev); | |
141 | WARN_ON(r < 0); | |
142 | return r < 0 ? r : 0; | |
143 | } | |
144 | ||
145 | static void rfbi_runtime_put(void) | |
146 | { | |
147 | int r; | |
148 | ||
149 | DSSDBG("rfbi_runtime_put\n"); | |
150 | ||
0eaf9f52 | 151 | r = pm_runtime_put_sync(&rfbi.pdev->dev); |
5be3aebd | 152 | WARN_ON(r < 0 && r != -ENOSYS); |
5c18adb3 TV |
153 | } |
154 | ||
c1e4535d | 155 | static void rfbi_bus_lock(void) |
773139f1 TV |
156 | { |
157 | down(&rfbi.bus_lock); | |
158 | } | |
773139f1 | 159 | |
c1e4535d | 160 | static void rfbi_bus_unlock(void) |
773139f1 TV |
161 | { |
162 | up(&rfbi.bus_lock); | |
163 | } | |
773139f1 | 164 | |
c1e4535d | 165 | static void rfbi_write_command(const void *buf, u32 len) |
5c18adb3 | 166 | { |
5c18adb3 TV |
167 | switch (rfbi.parallelmode) { |
168 | case OMAP_DSS_RFBI_PARALLELMODE_8: | |
169 | { | |
170 | const u8 *b = buf; | |
171 | for (; len; len--) | |
172 | rfbi_write_reg(RFBI_CMD, *b++); | |
173 | break; | |
174 | } | |
175 | ||
176 | case OMAP_DSS_RFBI_PARALLELMODE_16: | |
177 | { | |
178 | const u16 *w = buf; | |
179 | BUG_ON(len & 1); | |
180 | for (; len; len -= 2) | |
181 | rfbi_write_reg(RFBI_CMD, *w++); | |
182 | break; | |
183 | } | |
184 | ||
185 | case OMAP_DSS_RFBI_PARALLELMODE_9: | |
186 | case OMAP_DSS_RFBI_PARALLELMODE_12: | |
187 | default: | |
188 | BUG(); | |
189 | } | |
5c18adb3 | 190 | } |
5c18adb3 | 191 | |
c1e4535d | 192 | static void rfbi_read_data(void *buf, u32 len) |
5c18adb3 | 193 | { |
5c18adb3 TV |
194 | switch (rfbi.parallelmode) { |
195 | case OMAP_DSS_RFBI_PARALLELMODE_8: | |
196 | { | |
197 | u8 *b = buf; | |
198 | for (; len; len--) { | |
199 | rfbi_write_reg(RFBI_READ, 0); | |
200 | *b++ = rfbi_read_reg(RFBI_READ); | |
201 | } | |
202 | break; | |
203 | } | |
204 | ||
205 | case OMAP_DSS_RFBI_PARALLELMODE_16: | |
206 | { | |
207 | u16 *w = buf; | |
208 | BUG_ON(len & ~1); | |
209 | for (; len; len -= 2) { | |
210 | rfbi_write_reg(RFBI_READ, 0); | |
211 | *w++ = rfbi_read_reg(RFBI_READ); | |
212 | } | |
213 | break; | |
214 | } | |
215 | ||
216 | case OMAP_DSS_RFBI_PARALLELMODE_9: | |
217 | case OMAP_DSS_RFBI_PARALLELMODE_12: | |
218 | default: | |
219 | BUG(); | |
220 | } | |
5c18adb3 | 221 | } |
5c18adb3 | 222 | |
c1e4535d | 223 | static void rfbi_write_data(const void *buf, u32 len) |
5c18adb3 | 224 | { |
5c18adb3 TV |
225 | switch (rfbi.parallelmode) { |
226 | case OMAP_DSS_RFBI_PARALLELMODE_8: | |
227 | { | |
228 | const u8 *b = buf; | |
229 | for (; len; len--) | |
230 | rfbi_write_reg(RFBI_PARAM, *b++); | |
231 | break; | |
232 | } | |
233 | ||
234 | case OMAP_DSS_RFBI_PARALLELMODE_16: | |
235 | { | |
236 | const u16 *w = buf; | |
237 | BUG_ON(len & 1); | |
238 | for (; len; len -= 2) | |
239 | rfbi_write_reg(RFBI_PARAM, *w++); | |
240 | break; | |
241 | } | |
242 | ||
243 | case OMAP_DSS_RFBI_PARALLELMODE_9: | |
244 | case OMAP_DSS_RFBI_PARALLELMODE_12: | |
245 | default: | |
246 | BUG(); | |
247 | ||
248 | } | |
5c18adb3 | 249 | } |
5c18adb3 | 250 | |
c1e4535d | 251 | static void rfbi_write_pixels(const void __iomem *buf, int scr_width, |
5c18adb3 TV |
252 | u16 x, u16 y, |
253 | u16 w, u16 h) | |
254 | { | |
255 | int start_offset = scr_width * y + x; | |
256 | int horiz_offset = scr_width - w; | |
257 | int i; | |
258 | ||
5c18adb3 TV |
259 | if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 && |
260 | rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) { | |
261 | const u16 __iomem *pd = buf; | |
262 | pd += start_offset; | |
263 | ||
264 | for (; h; --h) { | |
265 | for (i = 0; i < w; ++i) { | |
266 | const u8 __iomem *b = (const u8 __iomem *)pd; | |
267 | rfbi_write_reg(RFBI_PARAM, __raw_readb(b+1)); | |
268 | rfbi_write_reg(RFBI_PARAM, __raw_readb(b+0)); | |
269 | ++pd; | |
270 | } | |
271 | pd += horiz_offset; | |
272 | } | |
273 | } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_24 && | |
274 | rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_8) { | |
275 | const u32 __iomem *pd = buf; | |
276 | pd += start_offset; | |
277 | ||
278 | for (; h; --h) { | |
279 | for (i = 0; i < w; ++i) { | |
280 | const u8 __iomem *b = (const u8 __iomem *)pd; | |
281 | rfbi_write_reg(RFBI_PARAM, __raw_readb(b+2)); | |
282 | rfbi_write_reg(RFBI_PARAM, __raw_readb(b+1)); | |
283 | rfbi_write_reg(RFBI_PARAM, __raw_readb(b+0)); | |
284 | ++pd; | |
285 | } | |
286 | pd += horiz_offset; | |
287 | } | |
288 | } else if (rfbi.datatype == OMAP_DSS_RFBI_DATATYPE_16 && | |
289 | rfbi.parallelmode == OMAP_DSS_RFBI_PARALLELMODE_16) { | |
290 | const u16 __iomem *pd = buf; | |
291 | pd += start_offset; | |
292 | ||
293 | for (; h; --h) { | |
294 | for (i = 0; i < w; ++i) { | |
295 | rfbi_write_reg(RFBI_PARAM, __raw_readw(pd)); | |
296 | ++pd; | |
297 | } | |
298 | pd += horiz_offset; | |
299 | } | |
300 | } else { | |
301 | BUG(); | |
302 | } | |
5c18adb3 | 303 | } |
5c18adb3 | 304 | |
43eab861 AT |
305 | static int rfbi_transfer_area(struct omap_dss_device *dssdev, |
306 | void (*callback)(void *data), void *data) | |
5c18adb3 TV |
307 | { |
308 | u32 l; | |
c47cdb30 | 309 | int r; |
7ae9a71e | 310 | struct omap_overlay_manager *mgr = rfbi.output.manager; |
6ff9dd5a AT |
311 | u16 width = rfbi.timings.x_res; |
312 | u16 height = rfbi.timings.y_res; | |
5c18adb3 TV |
313 | |
314 | /*BUG_ON(callback == 0);*/ | |
315 | BUG_ON(rfbi.framedone_callback != NULL); | |
316 | ||
317 | DSSDBG("rfbi_transfer_area %dx%d\n", width, height); | |
318 | ||
1db39c0a | 319 | dss_mgr_set_timings(mgr, &rfbi.timings); |
5c18adb3 | 320 | |
1db39c0a | 321 | r = dss_mgr_enable(mgr); |
c47cdb30 AT |
322 | if (r) |
323 | return r; | |
5c18adb3 TV |
324 | |
325 | rfbi.framedone_callback = callback; | |
326 | rfbi.framedone_callback_data = data; | |
327 | ||
5c18adb3 TV |
328 | rfbi_write_reg(RFBI_PIXEL_CNT, width * height); |
329 | ||
330 | l = rfbi_read_reg(RFBI_CONTROL); | |
331 | l = FLD_MOD(l, 1, 0, 0); /* enable */ | |
332 | if (!rfbi.te_enabled) | |
333 | l = FLD_MOD(l, 1, 4, 4); /* ITE */ | |
334 | ||
5c18adb3 | 335 | rfbi_write_reg(RFBI_CONTROL, l); |
c47cdb30 AT |
336 | |
337 | return 0; | |
5c18adb3 TV |
338 | } |
339 | ||
1550202d | 340 | static void framedone_callback(void *data) |
5c18adb3 TV |
341 | { |
342 | void (*callback)(void *data); | |
343 | ||
344 | DSSDBG("FRAMEDONE\n"); | |
345 | ||
5c18adb3 TV |
346 | REG_FLD_MOD(RFBI_CONTROL, 0, 0, 0); |
347 | ||
5c18adb3 TV |
348 | callback = rfbi.framedone_callback; |
349 | rfbi.framedone_callback = NULL; | |
350 | ||
18946f62 TV |
351 | if (callback != NULL) |
352 | callback(rfbi.framedone_callback_data); | |
5c18adb3 TV |
353 | } |
354 | ||
355 | #if 1 /* VERBOSE */ | |
356 | static void rfbi_print_timings(void) | |
357 | { | |
358 | u32 l; | |
359 | u32 time; | |
360 | ||
361 | l = rfbi_read_reg(RFBI_CONFIG(0)); | |
362 | time = 1000000000 / rfbi.l4_khz; | |
363 | if (l & (1 << 4)) | |
364 | time *= 2; | |
365 | ||
366 | DSSDBG("Tick time %u ps\n", time); | |
367 | l = rfbi_read_reg(RFBI_ONOFF_TIME(0)); | |
368 | DSSDBG("CSONTIME %d, CSOFFTIME %d, WEONTIME %d, WEOFFTIME %d, " | |
369 | "REONTIME %d, REOFFTIME %d\n", | |
370 | l & 0x0f, (l >> 4) & 0x3f, (l >> 10) & 0x0f, (l >> 14) & 0x3f, | |
371 | (l >> 20) & 0x0f, (l >> 24) & 0x3f); | |
372 | ||
373 | l = rfbi_read_reg(RFBI_CYCLE_TIME(0)); | |
374 | DSSDBG("WECYCLETIME %d, RECYCLETIME %d, CSPULSEWIDTH %d, " | |
375 | "ACCESSTIME %d\n", | |
376 | (l & 0x3f), (l >> 6) & 0x3f, (l >> 12) & 0x3f, | |
377 | (l >> 22) & 0x3f); | |
378 | } | |
379 | #else | |
380 | static void rfbi_print_timings(void) {} | |
381 | #endif | |
382 | ||
383 | ||
384 | ||
385 | ||
386 | static u32 extif_clk_period; | |
387 | ||
388 | static inline unsigned long round_to_extif_ticks(unsigned long ps, int div) | |
389 | { | |
390 | int bus_tick = extif_clk_period * div; | |
391 | return (ps + bus_tick - 1) / bus_tick * bus_tick; | |
392 | } | |
393 | ||
394 | static int calc_reg_timing(struct rfbi_timings *t, int div) | |
395 | { | |
396 | t->clk_div = div; | |
397 | ||
398 | t->cs_on_time = round_to_extif_ticks(t->cs_on_time, div); | |
399 | ||
400 | t->we_on_time = round_to_extif_ticks(t->we_on_time, div); | |
401 | t->we_off_time = round_to_extif_ticks(t->we_off_time, div); | |
402 | t->we_cycle_time = round_to_extif_ticks(t->we_cycle_time, div); | |
403 | ||
404 | t->re_on_time = round_to_extif_ticks(t->re_on_time, div); | |
405 | t->re_off_time = round_to_extif_ticks(t->re_off_time, div); | |
406 | t->re_cycle_time = round_to_extif_ticks(t->re_cycle_time, div); | |
407 | ||
408 | t->access_time = round_to_extif_ticks(t->access_time, div); | |
409 | t->cs_off_time = round_to_extif_ticks(t->cs_off_time, div); | |
410 | t->cs_pulse_width = round_to_extif_ticks(t->cs_pulse_width, div); | |
411 | ||
412 | DSSDBG("[reg]cson %d csoff %d reon %d reoff %d\n", | |
413 | t->cs_on_time, t->cs_off_time, t->re_on_time, t->re_off_time); | |
414 | DSSDBG("[reg]weon %d weoff %d recyc %d wecyc %d\n", | |
415 | t->we_on_time, t->we_off_time, t->re_cycle_time, | |
416 | t->we_cycle_time); | |
417 | DSSDBG("[reg]rdaccess %d cspulse %d\n", | |
418 | t->access_time, t->cs_pulse_width); | |
419 | ||
420 | return rfbi_convert_timings(t); | |
421 | } | |
422 | ||
423 | static int calc_extif_timings(struct rfbi_timings *t) | |
424 | { | |
425 | u32 max_clk_div; | |
426 | int div; | |
427 | ||
428 | rfbi_get_clk_info(&extif_clk_period, &max_clk_div); | |
429 | for (div = 1; div <= max_clk_div; div++) { | |
430 | if (calc_reg_timing(t, div) == 0) | |
431 | break; | |
432 | } | |
433 | ||
434 | if (div <= max_clk_div) | |
435 | return 0; | |
436 | ||
437 | DSSERR("can't setup timings\n"); | |
438 | return -1; | |
439 | } | |
440 | ||
441 | ||
c42ced63 | 442 | static void rfbi_set_timings(int rfbi_module, struct rfbi_timings *t) |
5c18adb3 TV |
443 | { |
444 | int r; | |
445 | ||
446 | if (!t->converted) { | |
447 | r = calc_extif_timings(t); | |
448 | if (r < 0) | |
449 | DSSERR("Failed to calc timings\n"); | |
450 | } | |
451 | ||
452 | BUG_ON(!t->converted); | |
453 | ||
5c18adb3 TV |
454 | rfbi_write_reg(RFBI_ONOFF_TIME(rfbi_module), t->tim[0]); |
455 | rfbi_write_reg(RFBI_CYCLE_TIME(rfbi_module), t->tim[1]); | |
456 | ||
457 | /* TIMEGRANULARITY */ | |
458 | REG_FLD_MOD(RFBI_CONFIG(rfbi_module), | |
459 | (t->tim[2] ? 1 : 0), 4, 4); | |
460 | ||
461 | rfbi_print_timings(); | |
5c18adb3 TV |
462 | } |
463 | ||
464 | static int ps_to_rfbi_ticks(int time, int div) | |
465 | { | |
466 | unsigned long tick_ps; | |
467 | int ret; | |
468 | ||
469 | /* Calculate in picosecs to yield more exact results */ | |
470 | tick_ps = 1000000000 / (rfbi.l4_khz) * div; | |
471 | ||
472 | ret = (time + tick_ps - 1) / tick_ps; | |
473 | ||
474 | return ret; | |
475 | } | |
476 | ||
5c18adb3 TV |
477 | static void rfbi_get_clk_info(u32 *clk_period, u32 *max_clk_div) |
478 | { | |
479 | *clk_period = 1000000000 / rfbi.l4_khz; | |
480 | *max_clk_div = 2; | |
481 | } | |
482 | ||
483 | static int rfbi_convert_timings(struct rfbi_timings *t) | |
484 | { | |
485 | u32 l; | |
486 | int reon, reoff, weon, weoff, cson, csoff, cs_pulse; | |
487 | int actim, recyc, wecyc; | |
488 | int div = t->clk_div; | |
489 | ||
490 | if (div <= 0 || div > 2) | |
491 | return -1; | |
492 | ||
493 | /* Make sure that after conversion it still holds that: | |
494 | * weoff > weon, reoff > reon, recyc >= reoff, wecyc >= weoff, | |
495 | * csoff > cson, csoff >= max(weoff, reoff), actim > reon | |
496 | */ | |
497 | weon = ps_to_rfbi_ticks(t->we_on_time, div); | |
498 | weoff = ps_to_rfbi_ticks(t->we_off_time, div); | |
499 | if (weoff <= weon) | |
500 | weoff = weon + 1; | |
501 | if (weon > 0x0f) | |
502 | return -1; | |
503 | if (weoff > 0x3f) | |
504 | return -1; | |
505 | ||
506 | reon = ps_to_rfbi_ticks(t->re_on_time, div); | |
507 | reoff = ps_to_rfbi_ticks(t->re_off_time, div); | |
508 | if (reoff <= reon) | |
509 | reoff = reon + 1; | |
510 | if (reon > 0x0f) | |
511 | return -1; | |
512 | if (reoff > 0x3f) | |
513 | return -1; | |
514 | ||
515 | cson = ps_to_rfbi_ticks(t->cs_on_time, div); | |
516 | csoff = ps_to_rfbi_ticks(t->cs_off_time, div); | |
517 | if (csoff <= cson) | |
518 | csoff = cson + 1; | |
519 | if (csoff < max(weoff, reoff)) | |
520 | csoff = max(weoff, reoff); | |
521 | if (cson > 0x0f) | |
522 | return -1; | |
523 | if (csoff > 0x3f) | |
524 | return -1; | |
525 | ||
526 | l = cson; | |
527 | l |= csoff << 4; | |
528 | l |= weon << 10; | |
529 | l |= weoff << 14; | |
530 | l |= reon << 20; | |
531 | l |= reoff << 24; | |
532 | ||
533 | t->tim[0] = l; | |
534 | ||
535 | actim = ps_to_rfbi_ticks(t->access_time, div); | |
536 | if (actim <= reon) | |
537 | actim = reon + 1; | |
538 | if (actim > 0x3f) | |
539 | return -1; | |
540 | ||
541 | wecyc = ps_to_rfbi_ticks(t->we_cycle_time, div); | |
542 | if (wecyc < weoff) | |
543 | wecyc = weoff; | |
544 | if (wecyc > 0x3f) | |
545 | return -1; | |
546 | ||
547 | recyc = ps_to_rfbi_ticks(t->re_cycle_time, div); | |
548 | if (recyc < reoff) | |
549 | recyc = reoff; | |
550 | if (recyc > 0x3f) | |
551 | return -1; | |
552 | ||
553 | cs_pulse = ps_to_rfbi_ticks(t->cs_pulse_width, div); | |
554 | if (cs_pulse > 0x3f) | |
555 | return -1; | |
556 | ||
557 | l = wecyc; | |
558 | l |= recyc << 6; | |
559 | l |= cs_pulse << 12; | |
560 | l |= actim << 22; | |
561 | ||
562 | t->tim[1] = l; | |
563 | ||
564 | t->tim[2] = div - 1; | |
565 | ||
566 | t->converted = 1; | |
567 | ||
568 | return 0; | |
569 | } | |
570 | ||
571 | /* xxx FIX module selection missing */ | |
c1e4535d | 572 | static int rfbi_setup_te(enum omap_rfbi_te_mode mode, |
5c18adb3 TV |
573 | unsigned hs_pulse_time, unsigned vs_pulse_time, |
574 | int hs_pol_inv, int vs_pol_inv, int extif_div) | |
575 | { | |
576 | int hs, vs; | |
577 | int min; | |
578 | u32 l; | |
579 | ||
580 | hs = ps_to_rfbi_ticks(hs_pulse_time, 1); | |
581 | vs = ps_to_rfbi_ticks(vs_pulse_time, 1); | |
582 | if (hs < 2) | |
583 | return -EDOM; | |
584 | if (mode == OMAP_DSS_RFBI_TE_MODE_2) | |
585 | min = 2; | |
586 | else /* OMAP_DSS_RFBI_TE_MODE_1 */ | |
587 | min = 4; | |
588 | if (vs < min) | |
589 | return -EDOM; | |
590 | if (vs == hs) | |
591 | return -EINVAL; | |
592 | rfbi.te_mode = mode; | |
593 | DSSDBG("setup_te: mode %d hs %d vs %d hs_inv %d vs_inv %d\n", | |
594 | mode, hs, vs, hs_pol_inv, vs_pol_inv); | |
595 | ||
5c18adb3 TV |
596 | rfbi_write_reg(RFBI_HSYNC_WIDTH, hs); |
597 | rfbi_write_reg(RFBI_VSYNC_WIDTH, vs); | |
598 | ||
599 | l = rfbi_read_reg(RFBI_CONFIG(0)); | |
600 | if (hs_pol_inv) | |
601 | l &= ~(1 << 21); | |
602 | else | |
603 | l |= 1 << 21; | |
604 | if (vs_pol_inv) | |
605 | l &= ~(1 << 20); | |
606 | else | |
607 | l |= 1 << 20; | |
5c18adb3 TV |
608 | |
609 | return 0; | |
610 | } | |
5c18adb3 TV |
611 | |
612 | /* xxx FIX module selection missing */ | |
c1e4535d | 613 | static int rfbi_enable_te(bool enable, unsigned line) |
5c18adb3 TV |
614 | { |
615 | u32 l; | |
616 | ||
617 | DSSDBG("te %d line %d mode %d\n", enable, line, rfbi.te_mode); | |
618 | if (line > (1 << 11) - 1) | |
619 | return -EINVAL; | |
620 | ||
5c18adb3 TV |
621 | l = rfbi_read_reg(RFBI_CONFIG(0)); |
622 | l &= ~(0x3 << 2); | |
623 | if (enable) { | |
624 | rfbi.te_enabled = 1; | |
625 | l |= rfbi.te_mode << 2; | |
626 | } else | |
627 | rfbi.te_enabled = 0; | |
628 | rfbi_write_reg(RFBI_CONFIG(0), l); | |
629 | rfbi_write_reg(RFBI_LINE_NUMBER, line); | |
5c18adb3 TV |
630 | |
631 | return 0; | |
632 | } | |
5c18adb3 | 633 | |
c1e4535d | 634 | static int rfbi_configure_bus(int rfbi_module, int bpp, int lines) |
5c18adb3 TV |
635 | { |
636 | u32 l; | |
637 | int cycle1 = 0, cycle2 = 0, cycle3 = 0; | |
638 | enum omap_rfbi_cycleformat cycleformat; | |
639 | enum omap_rfbi_datatype datatype; | |
640 | enum omap_rfbi_parallelmode parallelmode; | |
641 | ||
642 | switch (bpp) { | |
643 | case 12: | |
644 | datatype = OMAP_DSS_RFBI_DATATYPE_12; | |
645 | break; | |
646 | case 16: | |
647 | datatype = OMAP_DSS_RFBI_DATATYPE_16; | |
648 | break; | |
649 | case 18: | |
650 | datatype = OMAP_DSS_RFBI_DATATYPE_18; | |
651 | break; | |
652 | case 24: | |
653 | datatype = OMAP_DSS_RFBI_DATATYPE_24; | |
654 | break; | |
655 | default: | |
656 | BUG(); | |
657 | return 1; | |
658 | } | |
659 | rfbi.datatype = datatype; | |
660 | ||
661 | switch (lines) { | |
662 | case 8: | |
663 | parallelmode = OMAP_DSS_RFBI_PARALLELMODE_8; | |
664 | break; | |
665 | case 9: | |
666 | parallelmode = OMAP_DSS_RFBI_PARALLELMODE_9; | |
667 | break; | |
668 | case 12: | |
669 | parallelmode = OMAP_DSS_RFBI_PARALLELMODE_12; | |
670 | break; | |
671 | case 16: | |
672 | parallelmode = OMAP_DSS_RFBI_PARALLELMODE_16; | |
673 | break; | |
674 | default: | |
675 | BUG(); | |
676 | return 1; | |
677 | } | |
678 | rfbi.parallelmode = parallelmode; | |
679 | ||
680 | if ((bpp % lines) == 0) { | |
681 | switch (bpp / lines) { | |
682 | case 1: | |
683 | cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_1_1; | |
684 | break; | |
685 | case 2: | |
686 | cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_2_1; | |
687 | break; | |
688 | case 3: | |
689 | cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_1; | |
690 | break; | |
691 | default: | |
692 | BUG(); | |
693 | return 1; | |
694 | } | |
695 | } else if ((2 * bpp % lines) == 0) { | |
696 | if ((2 * bpp / lines) == 3) | |
697 | cycleformat = OMAP_DSS_RFBI_CYCLEFORMAT_3_2; | |
698 | else { | |
699 | BUG(); | |
700 | return 1; | |
701 | } | |
702 | } else { | |
703 | BUG(); | |
704 | return 1; | |
705 | } | |
706 | ||
707 | switch (cycleformat) { | |
708 | case OMAP_DSS_RFBI_CYCLEFORMAT_1_1: | |
709 | cycle1 = lines; | |
710 | break; | |
711 | ||
712 | case OMAP_DSS_RFBI_CYCLEFORMAT_2_1: | |
713 | cycle1 = lines; | |
714 | cycle2 = lines; | |
715 | break; | |
716 | ||
717 | case OMAP_DSS_RFBI_CYCLEFORMAT_3_1: | |
718 | cycle1 = lines; | |
719 | cycle2 = lines; | |
720 | cycle3 = lines; | |
721 | break; | |
722 | ||
723 | case OMAP_DSS_RFBI_CYCLEFORMAT_3_2: | |
724 | cycle1 = lines; | |
725 | cycle2 = (lines / 2) | ((lines / 2) << 16); | |
726 | cycle3 = (lines << 16); | |
727 | break; | |
728 | } | |
729 | ||
5c18adb3 TV |
730 | REG_FLD_MOD(RFBI_CONTROL, 0, 3, 2); /* clear CS */ |
731 | ||
732 | l = 0; | |
733 | l |= FLD_VAL(parallelmode, 1, 0); | |
734 | l |= FLD_VAL(0, 3, 2); /* TRIGGERMODE: ITE */ | |
735 | l |= FLD_VAL(0, 4, 4); /* TIMEGRANULARITY */ | |
736 | l |= FLD_VAL(datatype, 6, 5); | |
737 | /* l |= FLD_VAL(2, 8, 7); */ /* L4FORMAT, 2pix/L4 */ | |
738 | l |= FLD_VAL(0, 8, 7); /* L4FORMAT, 1pix/L4 */ | |
739 | l |= FLD_VAL(cycleformat, 10, 9); | |
740 | l |= FLD_VAL(0, 12, 11); /* UNUSEDBITS */ | |
741 | l |= FLD_VAL(0, 16, 16); /* A0POLARITY */ | |
742 | l |= FLD_VAL(0, 17, 17); /* REPOLARITY */ | |
743 | l |= FLD_VAL(0, 18, 18); /* WEPOLARITY */ | |
744 | l |= FLD_VAL(0, 19, 19); /* CSPOLARITY */ | |
745 | l |= FLD_VAL(1, 20, 20); /* TE_VSYNC_POLARITY */ | |
746 | l |= FLD_VAL(1, 21, 21); /* HSYNCPOLARITY */ | |
747 | rfbi_write_reg(RFBI_CONFIG(rfbi_module), l); | |
748 | ||
749 | rfbi_write_reg(RFBI_DATA_CYCLE1(rfbi_module), cycle1); | |
750 | rfbi_write_reg(RFBI_DATA_CYCLE2(rfbi_module), cycle2); | |
751 | rfbi_write_reg(RFBI_DATA_CYCLE3(rfbi_module), cycle3); | |
752 | ||
753 | ||
754 | l = rfbi_read_reg(RFBI_CONTROL); | |
755 | l = FLD_MOD(l, rfbi_module+1, 3, 2); /* Select CSx */ | |
756 | l = FLD_MOD(l, 0, 1, 1); /* clear bypass */ | |
757 | rfbi_write_reg(RFBI_CONTROL, l); | |
758 | ||
759 | ||
760 | DSSDBG("RFBI config: bpp %d, lines %d, cycles: 0x%x 0x%x 0x%x\n", | |
761 | bpp, lines, cycle1, cycle2, cycle3); | |
762 | ||
5c18adb3 TV |
763 | return 0; |
764 | } | |
1d5952a8 | 765 | |
c1e4535d | 766 | static int rfbi_configure(struct omap_dss_device *dssdev) |
1d5952a8 | 767 | { |
c1e4535d | 768 | return rfbi_configure_bus(dssdev->phy.rfbi.channel, rfbi.pixel_size, |
475989b7 | 769 | rfbi.data_lines); |
1d5952a8 | 770 | } |
5c18adb3 | 771 | |
c1e4535d | 772 | static int rfbi_update(struct omap_dss_device *dssdev, void (*callback)(void *), |
43eab861 | 773 | void *data) |
5c18adb3 | 774 | { |
43eab861 | 775 | return rfbi_transfer_area(dssdev, callback, data); |
5c18adb3 TV |
776 | } |
777 | ||
c1e4535d | 778 | static void rfbi_set_size(struct omap_dss_device *dssdev, u16 w, u16 h) |
6ff9dd5a AT |
779 | { |
780 | rfbi.timings.x_res = w; | |
781 | rfbi.timings.y_res = h; | |
782 | } | |
6ff9dd5a | 783 | |
c1e4535d | 784 | static void rfbi_set_pixel_size(struct omap_dss_device *dssdev, int pixel_size) |
b02875be AT |
785 | { |
786 | rfbi.pixel_size = pixel_size; | |
787 | } | |
b02875be | 788 | |
c1e4535d | 789 | static void rfbi_set_data_lines(struct omap_dss_device *dssdev, int data_lines) |
475989b7 AT |
790 | { |
791 | rfbi.data_lines = data_lines; | |
792 | } | |
475989b7 | 793 | |
c1e4535d | 794 | static void rfbi_set_interface_timings(struct omap_dss_device *dssdev, |
6e883324 AT |
795 | struct rfbi_timings *timings) |
796 | { | |
797 | rfbi.intf_timings = *timings; | |
798 | } | |
6e883324 | 799 | |
e40402cf | 800 | static void rfbi_dump_regs(struct seq_file *s) |
5c18adb3 TV |
801 | { |
802 | #define DUMPREG(r) seq_printf(s, "%-35s %08x\n", #r, rfbi_read_reg(r)) | |
803 | ||
4fbafaf3 TV |
804 | if (rfbi_runtime_get()) |
805 | return; | |
5c18adb3 TV |
806 | |
807 | DUMPREG(RFBI_REVISION); | |
808 | DUMPREG(RFBI_SYSCONFIG); | |
809 | DUMPREG(RFBI_SYSSTATUS); | |
810 | DUMPREG(RFBI_CONTROL); | |
811 | DUMPREG(RFBI_PIXEL_CNT); | |
812 | DUMPREG(RFBI_LINE_NUMBER); | |
813 | DUMPREG(RFBI_CMD); | |
814 | DUMPREG(RFBI_PARAM); | |
815 | DUMPREG(RFBI_DATA); | |
816 | DUMPREG(RFBI_READ); | |
817 | DUMPREG(RFBI_STATUS); | |
818 | ||
819 | DUMPREG(RFBI_CONFIG(0)); | |
820 | DUMPREG(RFBI_ONOFF_TIME(0)); | |
821 | DUMPREG(RFBI_CYCLE_TIME(0)); | |
822 | DUMPREG(RFBI_DATA_CYCLE1(0)); | |
823 | DUMPREG(RFBI_DATA_CYCLE2(0)); | |
824 | DUMPREG(RFBI_DATA_CYCLE3(0)); | |
825 | ||
826 | DUMPREG(RFBI_CONFIG(1)); | |
827 | DUMPREG(RFBI_ONOFF_TIME(1)); | |
828 | DUMPREG(RFBI_CYCLE_TIME(1)); | |
829 | DUMPREG(RFBI_DATA_CYCLE1(1)); | |
830 | DUMPREG(RFBI_DATA_CYCLE2(1)); | |
831 | DUMPREG(RFBI_DATA_CYCLE3(1)); | |
832 | ||
833 | DUMPREG(RFBI_VSYNC_WIDTH); | |
834 | DUMPREG(RFBI_HSYNC_WIDTH); | |
835 | ||
4fbafaf3 | 836 | rfbi_runtime_put(); |
5c18adb3 TV |
837 | #undef DUMPREG |
838 | } | |
839 | ||
bc2e60a6 AT |
840 | static void rfbi_config_lcd_manager(struct omap_dss_device *dssdev) |
841 | { | |
7ae9a71e | 842 | struct omap_overlay_manager *mgr = rfbi.output.manager; |
bc2e60a6 AT |
843 | struct dss_lcd_mgr_config mgr_config; |
844 | ||
845 | mgr_config.io_pad_mode = DSS_IO_PAD_MODE_RFBI; | |
846 | ||
847 | mgr_config.stallmode = true; | |
848 | /* Do we need fifohandcheck for RFBI? */ | |
849 | mgr_config.fifohandcheck = false; | |
850 | ||
b02875be | 851 | mgr_config.video_port_width = rfbi.pixel_size; |
bc2e60a6 AT |
852 | mgr_config.lcden_sig_polarity = 0; |
853 | ||
1db39c0a | 854 | dss_mgr_set_lcd_config(mgr, &mgr_config); |
6ff9dd5a AT |
855 | |
856 | /* | |
857 | * Set rfbi.timings with default values, the x_res and y_res fields | |
858 | * are expected to be already configured by the panel driver via | |
859 | * omapdss_rfbi_set_size() | |
860 | */ | |
861 | rfbi.timings.hsw = 1; | |
862 | rfbi.timings.hfp = 1; | |
863 | rfbi.timings.hbp = 1; | |
864 | rfbi.timings.vsw = 1; | |
865 | rfbi.timings.vfp = 0; | |
866 | rfbi.timings.vbp = 0; | |
867 | ||
868 | rfbi.timings.interlace = false; | |
869 | rfbi.timings.hsync_level = OMAPDSS_SIG_ACTIVE_HIGH; | |
870 | rfbi.timings.vsync_level = OMAPDSS_SIG_ACTIVE_HIGH; | |
871 | rfbi.timings.data_pclk_edge = OMAPDSS_DRIVE_SIG_RISING_EDGE; | |
872 | rfbi.timings.de_level = OMAPDSS_SIG_ACTIVE_HIGH; | |
7a16360d | 873 | rfbi.timings.sync_pclk_edge = OMAPDSS_DRIVE_SIG_FALLING_EDGE; |
6ff9dd5a | 874 | |
1db39c0a | 875 | dss_mgr_set_timings(mgr, &rfbi.timings); |
bc2e60a6 AT |
876 | } |
877 | ||
c1e4535d | 878 | static int rfbi_display_enable(struct omap_dss_device *dssdev) |
5c18adb3 | 879 | { |
1f68d9c4 | 880 | struct omap_dss_device *out = &rfbi.output; |
5c18adb3 TV |
881 | int r; |
882 | ||
b742648c | 883 | if (out->manager == NULL) { |
1db39c0a | 884 | DSSERR("failed to enable display: no output/manager\n"); |
05e1d606 TV |
885 | return -ENODEV; |
886 | } | |
887 | ||
4fbafaf3 TV |
888 | r = rfbi_runtime_get(); |
889 | if (r) | |
890 | return r; | |
5be685fa | 891 | |
1550202d TV |
892 | r = dss_mgr_register_framedone_handler(out->manager, |
893 | framedone_callback, NULL); | |
5c18adb3 TV |
894 | if (r) { |
895 | DSSERR("can't get FRAMEDONE irq\n"); | |
896 | goto err1; | |
897 | } | |
898 | ||
bc2e60a6 | 899 | rfbi_config_lcd_manager(dssdev); |
5c18adb3 | 900 | |
c1e4535d | 901 | rfbi_configure_bus(dssdev->phy.rfbi.channel, rfbi.pixel_size, |
475989b7 | 902 | rfbi.data_lines); |
5c18adb3 | 903 | |
6e883324 | 904 | rfbi_set_timings(dssdev->phy.rfbi.channel, &rfbi.intf_timings); |
5c18adb3 | 905 | |
5c18adb3 | 906 | return 0; |
5c18adb3 | 907 | err1: |
4fbafaf3 | 908 | rfbi_runtime_put(); |
5c18adb3 TV |
909 | return r; |
910 | } | |
911 | ||
c1e4535d | 912 | static void rfbi_display_disable(struct omap_dss_device *dssdev) |
5c18adb3 | 913 | { |
1f68d9c4 | 914 | struct omap_dss_device *out = &rfbi.output; |
1550202d TV |
915 | |
916 | dss_mgr_unregister_framedone_handler(out->manager, | |
917 | framedone_callback, NULL); | |
5be685fa | 918 | |
4fbafaf3 | 919 | rfbi_runtime_put(); |
5c18adb3 TV |
920 | } |
921 | ||
9abf7de1 | 922 | static int rfbi_init_display(struct omap_dss_device *dssdev) |
5c18adb3 | 923 | { |
5c18adb3 | 924 | rfbi.dssdev[dssdev->phy.rfbi.channel] = dssdev; |
5c18adb3 TV |
925 | return 0; |
926 | } | |
3448d500 | 927 | |
9abf7de1 | 928 | static void rfbi_init_output(struct platform_device *pdev) |
81b87f51 | 929 | { |
1f68d9c4 | 930 | struct omap_dss_device *out = &rfbi.output; |
81b87f51 | 931 | |
1f68d9c4 | 932 | out->dev = &pdev->dev; |
81b87f51 | 933 | out->id = OMAP_DSS_OUTPUT_DBI; |
1f68d9c4 | 934 | out->output_type = OMAP_DISPLAY_TYPE_DBI; |
7286a08f | 935 | out->name = "rfbi.0"; |
2eea5ae6 | 936 | out->dispc_channel = OMAP_DSS_CHANNEL_LCD; |
b7328e14 | 937 | out->owner = THIS_MODULE; |
81b87f51 | 938 | |
5d47dbc8 | 939 | omapdss_register_output(out); |
81b87f51 AT |
940 | } |
941 | ||
ede92695 | 942 | static void rfbi_uninit_output(struct platform_device *pdev) |
81b87f51 | 943 | { |
1f68d9c4 | 944 | struct omap_dss_device *out = &rfbi.output; |
81b87f51 | 945 | |
5d47dbc8 | 946 | omapdss_unregister_output(out); |
81b87f51 AT |
947 | } |
948 | ||
3448d500 | 949 | /* RFBI HW IP initialisation */ |
736e60dd | 950 | static int rfbi_bind(struct device *dev, struct device *master, void *data) |
3448d500 | 951 | { |
736e60dd | 952 | struct platform_device *pdev = to_platform_device(dev); |
3448d500 | 953 | u32 rev; |
ea9da36a | 954 | struct resource *rfbi_mem; |
4fbafaf3 | 955 | struct clk *clk; |
38f3daf6 | 956 | int r; |
3448d500 SG |
957 | |
958 | rfbi.pdev = pdev; | |
959 | ||
773139f1 | 960 | sema_init(&rfbi.bus_lock, 1); |
3448d500 | 961 | |
ea9da36a SG |
962 | rfbi_mem = platform_get_resource(rfbi.pdev, IORESOURCE_MEM, 0); |
963 | if (!rfbi_mem) { | |
964 | DSSERR("can't get IORESOURCE_MEM RFBI\n"); | |
cd3b3449 | 965 | return -EINVAL; |
ea9da36a | 966 | } |
cd3b3449 | 967 | |
6e2a14d2 JL |
968 | rfbi.base = devm_ioremap(&pdev->dev, rfbi_mem->start, |
969 | resource_size(rfbi_mem)); | |
3448d500 SG |
970 | if (!rfbi.base) { |
971 | DSSERR("can't ioremap RFBI\n"); | |
cd3b3449 | 972 | return -ENOMEM; |
3448d500 SG |
973 | } |
974 | ||
bfe4f8d3 | 975 | clk = clk_get(&pdev->dev, "ick"); |
4fbafaf3 TV |
976 | if (IS_ERR(clk)) { |
977 | DSSERR("can't get ick\n"); | |
cd3b3449 | 978 | return PTR_ERR(clk); |
4fbafaf3 TV |
979 | } |
980 | ||
981 | rfbi.l4_khz = clk_get_rate(clk) / 1000; | |
3448d500 | 982 | |
4fbafaf3 | 983 | clk_put(clk); |
3448d500 | 984 | |
cd3b3449 TV |
985 | pm_runtime_enable(&pdev->dev); |
986 | ||
987 | r = rfbi_runtime_get(); | |
988 | if (r) | |
989 | goto err_runtime_get; | |
990 | ||
991 | msleep(10); | |
992 | ||
3448d500 | 993 | rev = rfbi_read_reg(RFBI_REVISION); |
a06b62f8 | 994 | dev_dbg(&pdev->dev, "OMAP RFBI rev %d.%d\n", |
3448d500 SG |
995 | FLD_GET(rev, 7, 4), FLD_GET(rev, 3, 0)); |
996 | ||
4fbafaf3 | 997 | rfbi_runtime_put(); |
3448d500 | 998 | |
e40402cf TV |
999 | dss_debugfs_create_file("rfbi", rfbi_dump_regs); |
1000 | ||
81b87f51 AT |
1001 | rfbi_init_output(pdev); |
1002 | ||
3448d500 | 1003 | return 0; |
4fbafaf3 | 1004 | |
cd3b3449 | 1005 | err_runtime_get: |
4fbafaf3 | 1006 | pm_runtime_disable(&pdev->dev); |
4fbafaf3 | 1007 | return r; |
3448d500 SG |
1008 | } |
1009 | ||
736e60dd | 1010 | static void rfbi_unbind(struct device *dev, struct device *master, void *data) |
3448d500 | 1011 | { |
736e60dd TV |
1012 | struct platform_device *pdev = to_platform_device(dev); |
1013 | ||
81b87f51 AT |
1014 | rfbi_uninit_output(pdev); |
1015 | ||
4fbafaf3 | 1016 | pm_runtime_disable(&pdev->dev); |
81b87f51 | 1017 | |
3448d500 SG |
1018 | return 0; |
1019 | } | |
1020 | ||
736e60dd TV |
1021 | static const struct component_ops rfbi_component_ops = { |
1022 | .bind = rfbi_bind, | |
1023 | .unbind = rfbi_unbind, | |
1024 | }; | |
1025 | ||
1026 | static int rfbi_probe(struct platform_device *pdev) | |
1027 | { | |
1028 | return component_add(&pdev->dev, &rfbi_component_ops); | |
1029 | } | |
1030 | ||
1031 | static int rfbi_remove(struct platform_device *pdev) | |
1032 | { | |
1033 | component_del(&pdev->dev, &rfbi_component_ops); | |
1034 | return 0; | |
1035 | } | |
1036 | ||
4fbafaf3 TV |
1037 | static int rfbi_runtime_suspend(struct device *dev) |
1038 | { | |
1039 | dispc_runtime_put(); | |
4fbafaf3 TV |
1040 | |
1041 | return 0; | |
1042 | } | |
1043 | ||
1044 | static int rfbi_runtime_resume(struct device *dev) | |
1045 | { | |
1046 | int r; | |
1047 | ||
4fbafaf3 TV |
1048 | r = dispc_runtime_get(); |
1049 | if (r < 0) | |
852f0838 | 1050 | return r; |
4fbafaf3 TV |
1051 | |
1052 | return 0; | |
4fbafaf3 TV |
1053 | } |
1054 | ||
1055 | static const struct dev_pm_ops rfbi_pm_ops = { | |
1056 | .runtime_suspend = rfbi_runtime_suspend, | |
1057 | .runtime_resume = rfbi_runtime_resume, | |
1058 | }; | |
1059 | ||
3448d500 | 1060 | static struct platform_driver omap_rfbihw_driver = { |
736e60dd TV |
1061 | .probe = rfbi_probe, |
1062 | .remove = rfbi_remove, | |
3448d500 SG |
1063 | .driver = { |
1064 | .name = "omapdss_rfbi", | |
4fbafaf3 | 1065 | .pm = &rfbi_pm_ops, |
422ccbd5 | 1066 | .suppress_bind_attrs = true, |
3448d500 SG |
1067 | }, |
1068 | }; | |
1069 | ||
6e7e8f06 | 1070 | int __init rfbi_init_platform_driver(void) |
3448d500 | 1071 | { |
9abf7de1 | 1072 | return platform_driver_register(&omap_rfbihw_driver); |
3448d500 SG |
1073 | } |
1074 | ||
ede92695 | 1075 | void rfbi_uninit_platform_driver(void) |
3448d500 | 1076 | { |
04c742c3 | 1077 | platform_driver_unregister(&omap_rfbihw_driver); |
3448d500 | 1078 | } |