blob: ca3c07930d075ca945390cbc79153e6f66a517fa [file] [log] [blame]
Pavel Machekd480ace2009-09-22 16:47:03 -07001/* drivers/video/msm_fb/mdp.c
2 *
3 * MSM MDP Interface (used by framebuffer core)
4 *
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07005 * Copyright (c) 2007-2011, Code Aurora Forum. All rights reserved.
Pavel Machekd480ace2009-09-22 16:47:03 -07006 * Copyright (C) 2007 Google Incorporated
7 *
8 * This software is licensed under the terms of the GNU General Public
9 * License version 2, as published by the Free Software Foundation, and
10 * may be copied, distributed, and modified under those terms.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 */
17
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070018#include <linux/module.h>
Pavel Machekd480ace2009-09-22 16:47:03 -070019#include <linux/kernel.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070020#include <linux/sched.h>
21#include <linux/time.h>
22#include <linux/init.h>
Pavel Machekd480ace2009-09-22 16:47:03 -070023#include <linux/interrupt.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070024#include <linux/spinlock.h>
25#include <linux/hrtimer.h>
Pavel Machekd480ace2009-09-22 16:47:03 -070026#include <linux/clk.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070027#include <mach/hardware.h>
28#include <linux/io.h>
29#include <linux/debugfs.h>
30#include <linux/delay.h>
31#include <linux/mutex.h>
32#include <linux/pm_runtime.h>
33#include <linux/regulator/consumer.h>
Pavel Machekd480ace2009-09-22 16:47:03 -070034
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070035#include <asm/system.h>
36#include <asm/mach-types.h>
37#include <linux/semaphore.h>
38#include <linux/uaccess.h>
39#include <mach/clk.h>
40#include "mdp.h"
41#include "msm_fb.h"
42#ifdef CONFIG_FB_MSM_MDP40
43#include "mdp4.h"
44#endif
45#include "mipi_dsi.h"
Pavel Machekd480ace2009-09-22 16:47:03 -070046
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070047uint32 mdp4_extn_disp;
Pavel Machekd480ace2009-09-22 16:47:03 -070048
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070049static struct clk *mdp_clk;
50static struct clk *mdp_pclk;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051static struct clk *mdp_lut_clk;
52int mdp_rev;
Pavel Machekd480ace2009-09-22 16:47:03 -070053
Ravishangar Kalyanam419051b2011-08-31 19:07:53 -070054static struct regulator *footswitch;
Pavel Machekd480ace2009-09-22 16:47:03 -070055
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070056struct completion mdp_ppp_comp;
57struct semaphore mdp_ppp_mutex;
58struct semaphore mdp_pipe_ctrl_mutex;
Pavel Machekd480ace2009-09-22 16:47:03 -070059
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070060unsigned long mdp_timer_duration = (HZ/20); /* 50 msecond */
Pavel Machekd480ace2009-09-22 16:47:03 -070061
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070062boolean mdp_ppp_waiting = FALSE;
63uint32 mdp_tv_underflow_cnt;
64uint32 mdp_lcdc_underflow_cnt;
65
66boolean mdp_current_clk_on = FALSE;
67boolean mdp_is_in_isr = FALSE;
68
69/*
70 * legacy mdp_in_processing is only for DMA2-MDDI
71 * this applies to DMA2 block only
72 */
73uint32 mdp_in_processing = FALSE;
74
75#ifdef CONFIG_FB_MSM_MDP40
76uint32 mdp_intr_mask = MDP4_ANY_INTR_MASK;
77#else
78uint32 mdp_intr_mask = MDP_ANY_INTR_MASK;
79#endif
80
81MDP_BLOCK_TYPE mdp_debug[MDP_MAX_BLOCK];
82
83atomic_t mdp_block_power_cnt[MDP_MAX_BLOCK];
84
85spinlock_t mdp_spin_lock;
86struct workqueue_struct *mdp_dma_wq; /*mdp dma wq */
87struct workqueue_struct *mdp_vsync_wq; /*mdp vsync wq */
88
89static struct workqueue_struct *mdp_pipe_ctrl_wq; /* mdp mdp pipe ctrl wq */
90static struct delayed_work mdp_pipe_ctrl_worker;
91
92static boolean mdp_suspended = FALSE;
93DEFINE_MUTEX(mdp_suspend_mutex);
94
95#ifdef CONFIG_FB_MSM_MDP40
96struct mdp_dma_data dma2_data;
97struct mdp_dma_data dma_s_data;
98struct mdp_dma_data dma_e_data;
99ulong mdp4_display_intf;
100#else
101static struct mdp_dma_data dma2_data;
102static struct mdp_dma_data dma_s_data;
103#ifndef CONFIG_FB_MSM_MDP303
104static struct mdp_dma_data dma_e_data;
105#endif
106#endif
107static struct mdp_dma_data dma3_data;
108
109extern ktime_t mdp_dma2_last_update_time;
110
111extern uint32 mdp_dma2_update_time_in_usec;
112extern int mdp_lcd_rd_cnt_offset_slow;
113extern int mdp_lcd_rd_cnt_offset_fast;
114extern int mdp_usec_diff_threshold;
115
116#ifdef CONFIG_FB_MSM_LCDC
117extern int first_pixel_start_x;
118extern int first_pixel_start_y;
119#endif
120
121#ifdef MSM_FB_ENABLE_DBGFS
122struct dentry *mdp_dir;
123#endif
124
125#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND)
126static int mdp_suspend(struct platform_device *pdev, pm_message_t state);
127#else
128#define mdp_suspend NULL
129#endif
130
131struct timeval mdp_dma2_timeval;
132struct timeval mdp_ppp_timeval;
133
134#ifdef CONFIG_HAS_EARLYSUSPEND
135static struct early_suspend early_suspend;
136#endif
137
138static u32 mdp_irq;
139
140static uint32 mdp_prim_panel_type = NO_PANEL;
141#ifndef CONFIG_FB_MSM_MDP22
142DEFINE_MUTEX(mdp_lut_push_sem);
143static int mdp_lut_i;
144static int mdp_lut_hw_update(struct fb_cmap *cmap)
Pavel Machekd480ace2009-09-22 16:47:03 -0700145{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700146 int i;
147 u16 *c[3];
148 u16 r, g, b;
Pavel Machekd480ace2009-09-22 16:47:03 -0700149
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700150 c[0] = cmap->green;
151 c[1] = cmap->blue;
152 c[2] = cmap->red;
Pavel Machekd480ace2009-09-22 16:47:03 -0700153
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700154 for (i = 0; i < cmap->len; i++) {
155 if (copy_from_user(&r, cmap->red++, sizeof(r)) ||
156 copy_from_user(&g, cmap->green++, sizeof(g)) ||
157 copy_from_user(&b, cmap->blue++, sizeof(b)))
158 return -EFAULT;
159
160#ifdef CONFIG_FB_MSM_MDP40
161 MDP_OUTP(MDP_BASE + 0x94800 +
162#else
163 MDP_OUTP(MDP_BASE + 0x93800 +
164#endif
165 (0x400*mdp_lut_i) + cmap->start*4 + i*4,
166 ((g & 0xff) |
167 ((b & 0xff) << 8) |
168 ((r & 0xff) << 16)));
Pavel Machekd480ace2009-09-22 16:47:03 -0700169 }
170
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700171 return 0;
Pavel Machekd480ace2009-09-22 16:47:03 -0700172}
173
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700174static int mdp_lut_push;
175static int mdp_lut_push_i;
176static int mdp_lut_update_nonlcdc(struct fb_info *info, struct fb_cmap *cmap)
Pavel Machekd480ace2009-09-22 16:47:03 -0700177{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700178 int ret;
179
180 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
181 ret = mdp_lut_hw_update(cmap);
182 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
183
184 if (ret)
185 return ret;
186
187 mutex_lock(&mdp_lut_push_sem);
188 mdp_lut_push = 1;
189 mdp_lut_push_i = mdp_lut_i;
190 mutex_unlock(&mdp_lut_push_sem);
191
192 mdp_lut_i = (mdp_lut_i + 1)%2;
193
194 return 0;
195}
196
197static int mdp_lut_update_lcdc(struct fb_info *info, struct fb_cmap *cmap)
198{
199 int ret;
200
201 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
202 ret = mdp_lut_hw_update(cmap);
203
204 if (ret) {
205 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
206 return ret;
Pavel Machekd480ace2009-09-22 16:47:03 -0700207 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700208
209 MDP_OUTP(MDP_BASE + 0x90070, (mdp_lut_i << 10) | 0x17);
210 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
211 mdp_lut_i = (mdp_lut_i + 1)%2;
212
213 return 0;
214}
215
216static void mdp_lut_enable(void)
217{
218 if (mdp_lut_push) {
219 mutex_lock(&mdp_lut_push_sem);
220 mdp_lut_push = 0;
221 MDP_OUTP(MDP_BASE + 0x90070,
222 (mdp_lut_push_i << 10) | 0x17);
223 mutex_unlock(&mdp_lut_push_sem);
224 }
225}
226
Ravishangar Kalyaname7833e22011-07-22 16:20:19 -0700227#define MDP_REV42_HIST_MAX_BIN 128
228#define MDP_REV41_HIST_MAX_BIN 32
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700229
230#ifdef CONFIG_FB_MSM_MDP40
Ravishangar Kalyanam8fef09a2011-08-09 17:36:23 -0700231unsigned int mdp_hist_frame_cnt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700232struct completion mdp_hist_comp;
233boolean mdp_is_hist_start = FALSE;
234#else
Ravishangar Kalyanam8fef09a2011-08-09 17:36:23 -0700235static unsigned int mdp_hist_frame_cnt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700236static struct completion mdp_hist_comp;
237static boolean mdp_is_hist_start = FALSE;
238#endif
239static DEFINE_MUTEX(mdp_hist_mutex);
240
241int mdp_histogram_ctrl(boolean en)
242{
243 unsigned long flag;
244 boolean hist_start;
245 spin_lock_irqsave(&mdp_spin_lock, flag);
246 hist_start = mdp_is_hist_start;
247 spin_unlock_irqrestore(&mdp_spin_lock, flag);
248
249 if (hist_start == TRUE) {
250 if (en == TRUE) {
251 mdp_enable_irq(MDP_HISTOGRAM_TERM);
Ravishangar Kalyanam8fef09a2011-08-09 17:36:23 -0700252 mdp_hist_frame_cnt = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700253 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
254#ifdef CONFIG_FB_MSM_MDP40
255 MDP_OUTP(MDP_BASE + 0x95010, 1);
256 MDP_OUTP(MDP_BASE + 0x9501c, INTR_HIST_DONE);
257 MDP_OUTP(MDP_BASE + 0x95004, 1);
258 MDP_OUTP(MDP_BASE + 0x95000, 1);
259#else
260 MDP_OUTP(MDP_BASE + 0x94004, 1);
261 MDP_OUTP(MDP_BASE + 0x94000, 1);
262#endif
263 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF,
264 FALSE);
265 } else
266 mdp_disable_irq(MDP_HISTOGRAM_TERM);
Pavel Machekd480ace2009-09-22 16:47:03 -0700267 }
268 return 0;
269}
270
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700271int mdp_start_histogram(struct fb_info *info)
Pavel Machekd480ace2009-09-22 16:47:03 -0700272{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700273 unsigned long flag;
Pavel Machekd480ace2009-09-22 16:47:03 -0700274
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700275 int ret = 0;
276 mutex_lock(&mdp_hist_mutex);
277 if (mdp_is_hist_start == TRUE) {
278 printk(KERN_ERR "%s histogram already started\n", __func__);
279 ret = -EPERM;
280 goto mdp_hist_start_err;
281 }
282
283 spin_lock_irqsave(&mdp_spin_lock, flag);
284 mdp_is_hist_start = TRUE;
285 spin_unlock_irqrestore(&mdp_spin_lock, flag);
286 mdp_enable_irq(MDP_HISTOGRAM_TERM);
Ravishangar Kalyanam8fef09a2011-08-09 17:36:23 -0700287 mdp_hist_frame_cnt = 1;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700288 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
289#ifdef CONFIG_FB_MSM_MDP40
290 MDP_OUTP(MDP_BASE + 0x95004, 1);
291 MDP_OUTP(MDP_BASE + 0x95000, 1);
292#else
293 MDP_OUTP(MDP_BASE + 0x94004, 1);
294 MDP_OUTP(MDP_BASE + 0x94000, 1);
295#endif
296 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
297
298mdp_hist_start_err:
299 mutex_unlock(&mdp_hist_mutex);
300 return ret;
301
302}
303int mdp_stop_histogram(struct fb_info *info)
304{
305 unsigned long flag;
306 int ret = 0;
307 mutex_lock(&mdp_hist_mutex);
308 if (!mdp_is_hist_start) {
309 printk(KERN_ERR "%s histogram already stopped\n", __func__);
310 ret = -EPERM;
311 goto mdp_hist_stop_err;
312 }
313 spin_lock_irqsave(&mdp_spin_lock, flag);
314 mdp_is_hist_start = FALSE;
315 spin_unlock_irqrestore(&mdp_spin_lock, flag);
316 /* disable the irq for histogram since we handled it
317 when the control reaches here */
318 mdp_disable_irq(MDP_HISTOGRAM_TERM);
319
320mdp_hist_stop_err:
321 mutex_unlock(&mdp_hist_mutex);
Pavel Machekd480ace2009-09-22 16:47:03 -0700322 return ret;
323}
324
Ravishangar Kalyanam8fef09a2011-08-09 17:36:23 -0700325static int mdp_copy_hist_data(struct mdp_histogram *hist)
Pavel Machekd480ace2009-09-22 16:47:03 -0700326{
Ravishangar Kalyanam8fef09a2011-08-09 17:36:23 -0700327 char *mdp_hist_base;
328 uint32 r_data_offset = 0x100, g_data_offset = 0x200;
329 uint32 b_data_offset = 0x300;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700330 int ret = 0;
Pavel Machekd480ace2009-09-22 16:47:03 -0700331
Ravishangar Kalyanam8fef09a2011-08-09 17:36:23 -0700332 mutex_lock(&mdp_hist_mutex);
333 if (mdp_rev >= MDP_REV_42) {
334 mdp_hist_base = MDP_BASE + 0x95000;
335 r_data_offset = 0x400;
336 g_data_offset = 0x800;
337 b_data_offset = 0xc00;
338 } else if (mdp_rev >= MDP_REV_40 && mdp_rev <= MDP_REV_41) {
339 mdp_hist_base = MDP_BASE + 0x95000;
340 } else if (mdp_rev >= MDP_REV_30 && mdp_rev <= MDP_REV_31) {
341 mdp_hist_base = MDP_BASE + 0x94000;
342 } else {
343 pr_err("%s(): Unsupported MDP rev %u\n", __func__, mdp_rev);
344 return -EPERM;
345 }
346
347 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
348 if (hist->r) {
349 ret = copy_to_user(hist->r, mdp_hist_base + r_data_offset,
350 hist->bin_cnt * 4);
351 if (ret)
352 goto hist_err;
353 }
354 if (hist->g) {
355 ret = copy_to_user(hist->g, mdp_hist_base + g_data_offset,
356 hist->bin_cnt * 4);
357 if (ret)
358 goto hist_err;
359 }
360 if (hist->b) {
361 ret = copy_to_user(hist->b, mdp_hist_base + b_data_offset,
362 hist->bin_cnt * 4);
363 if (ret)
364 goto hist_err;
365 }
366
367 if (mdp_is_hist_start == TRUE) {
368 MDP_OUTP(mdp_hist_base + 0x004,
369 mdp_hist_frame_cnt);
370 MDP_OUTP(mdp_hist_base, 1);
371 }
372 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
373 mutex_unlock(&mdp_hist_mutex);
374 return 0;
375
376hist_err:
377 printk(KERN_ERR "%s: invalid hist buffer\n", __func__);
378 return ret;
379}
380
381static int mdp_do_histogram(struct fb_info *info, struct mdp_histogram *hist)
382{
Ravishangar Kalyaname7833e22011-07-22 16:20:19 -0700383 if (!hist->frame_cnt || (hist->bin_cnt == 0))
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700384 return -EINVAL;
Ravishangar Kalyaname7833e22011-07-22 16:20:19 -0700385
386 if ((mdp_rev <= MDP_REV_41 && hist->bin_cnt > MDP_REV41_HIST_MAX_BIN)
387 || (mdp_rev == MDP_REV_42 &&
388 hist->bin_cnt > MDP_REV42_HIST_MAX_BIN))
389 return -EINVAL;
390
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700391 mutex_lock(&mdp_hist_mutex);
392 if (!mdp_is_hist_start) {
393 printk(KERN_ERR "%s histogram not started\n", __func__);
394 mutex_unlock(&mdp_hist_mutex);
395 return -EPERM;
396 }
397 mutex_unlock(&mdp_hist_mutex);
Pavel Machekd480ace2009-09-22 16:47:03 -0700398
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700399 INIT_COMPLETION(mdp_hist_comp);
Ravishangar Kalyanam8fef09a2011-08-09 17:36:23 -0700400 mdp_hist_frame_cnt = hist->frame_cnt;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700401 wait_for_completion_killable(&mdp_hist_comp);
402
Ravishangar Kalyanam8fef09a2011-08-09 17:36:23 -0700403 return mdp_copy_hist_data(hist);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700404}
405#endif
406
407/* Returns < 0 on error, 0 on timeout, or > 0 on successful wait */
408
409int mdp_ppp_pipe_wait(void)
410{
411 int ret = 1;
412
413 /* wait 5 seconds for the operation to complete before declaring
414 the MDP hung */
415
416 if (mdp_ppp_waiting == TRUE) {
417 ret = wait_for_completion_interruptible_timeout(&mdp_ppp_comp,
418 5 * HZ);
419
420 if (!ret)
421 printk(KERN_ERR "%s: Timed out waiting for the MDP.\n",
422 __func__);
Pavel Machekd480ace2009-09-22 16:47:03 -0700423 }
424
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700425 return ret;
426}
Pavel Machekd480ace2009-09-22 16:47:03 -0700427
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700428static DEFINE_SPINLOCK(mdp_lock);
429static int mdp_irq_mask;
430static int mdp_irq_enabled;
Pavel Machekd480ace2009-09-22 16:47:03 -0700431
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700432/*
433 * mdp_enable_irq: can not be called from isr
434 */
435void mdp_enable_irq(uint32 term)
436{
437 unsigned long irq_flags;
438
439 spin_lock_irqsave(&mdp_lock, irq_flags);
440 if (mdp_irq_mask & term) {
441 printk(KERN_ERR "%s: MDP IRQ term-0x%x is already set, mask=%x irq=%d\n",
442 __func__, term, mdp_irq_mask, mdp_irq_enabled);
443 } else {
444 mdp_irq_mask |= term;
445 if (mdp_irq_mask && !mdp_irq_enabled) {
446 mdp_irq_enabled = 1;
447 enable_irq(mdp_irq);
448 }
449 }
Pavel Machekd480ace2009-09-22 16:47:03 -0700450 spin_unlock_irqrestore(&mdp_lock, irq_flags);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700451}
452
453/*
454 * mdp_disable_irq: can not be called from isr
455 */
456void mdp_disable_irq(uint32 term)
457{
458 unsigned long irq_flags;
459
460 spin_lock_irqsave(&mdp_lock, irq_flags);
461 if (!(mdp_irq_mask & term)) {
462 printk(KERN_ERR "%s: MDP IRQ term-0x%x is NOT set, mask=%x irq=%d\n",
463 __func__, term, mdp_irq_mask, mdp_irq_enabled);
464 } else {
465 mdp_irq_mask &= ~term;
466 if (!mdp_irq_mask && mdp_irq_enabled) {
467 mdp_irq_enabled = 0;
468 disable_irq(mdp_irq);
469 }
470 }
471 spin_unlock_irqrestore(&mdp_lock, irq_flags);
472}
473
474void mdp_disable_irq_nosync(uint32 term)
475{
476 spin_lock(&mdp_lock);
477 if (!(mdp_irq_mask & term)) {
478 printk(KERN_ERR "%s: MDP IRQ term-0x%x is NOT set, mask=%x irq=%d\n",
479 __func__, term, mdp_irq_mask, mdp_irq_enabled);
480 } else {
481 mdp_irq_mask &= ~term;
482 if (!mdp_irq_mask && mdp_irq_enabled) {
483 mdp_irq_enabled = 0;
484 disable_irq_nosync(mdp_irq);
485 }
486 }
487 spin_unlock(&mdp_lock);
488}
489
490void mdp_pipe_kickoff(uint32 term, struct msm_fb_data_type *mfd)
491{
492 /* complete all the writes before starting */
493 wmb();
494
495 /* kick off PPP engine */
496 if (term == MDP_PPP_TERM) {
497 if (mdp_debug[MDP_PPP_BLOCK])
498 jiffies_to_timeval(jiffies, &mdp_ppp_timeval);
499
500 /* let's turn on PPP block */
501 mdp_pipe_ctrl(MDP_PPP_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
502
503 mdp_enable_irq(term);
504 INIT_COMPLETION(mdp_ppp_comp);
505 mdp_ppp_waiting = TRUE;
506 outpdw(MDP_BASE + 0x30, 0x1000);
507 wait_for_completion_killable(&mdp_ppp_comp);
508 mdp_disable_irq(term);
509
510 if (mdp_debug[MDP_PPP_BLOCK]) {
511 struct timeval now;
512
513 jiffies_to_timeval(jiffies, &now);
514 mdp_ppp_timeval.tv_usec =
515 now.tv_usec - mdp_ppp_timeval.tv_usec;
516 MSM_FB_DEBUG("MDP-PPP: %d\n",
517 (int)mdp_ppp_timeval.tv_usec);
518 }
519 } else if (term == MDP_DMA2_TERM) {
520 if (mdp_debug[MDP_DMA2_BLOCK]) {
521 MSM_FB_DEBUG("MDP-DMA2: %d\n",
522 (int)mdp_dma2_timeval.tv_usec);
523 jiffies_to_timeval(jiffies, &mdp_dma2_timeval);
524 }
525 /* DMA update timestamp */
526 mdp_dma2_last_update_time = ktime_get_real();
527 /* let's turn on DMA2 block */
528#if 0
529 mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
530#endif
531#ifdef CONFIG_FB_MSM_MDP22
532 outpdw(MDP_CMD_DEBUG_ACCESS_BASE + 0x0044, 0x0);/* start DMA */
533#else
534 mdp_lut_enable();
535
536#ifdef CONFIG_FB_MSM_MDP40
537 outpdw(MDP_BASE + 0x000c, 0x0); /* start DMA */
538#else
539 outpdw(MDP_BASE + 0x0044, 0x0); /* start DMA */
540
541#ifdef CONFIG_FB_MSM_MDP303
542
543#ifdef CONFIG_FB_MSM_MIPI_DSI
kuogee hsieh8717a172011-09-05 09:57:58 -0700544 mipi_dsi_cmd_mdp_start();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700545#endif
546
547#endif
548
549#endif
550#endif
551#ifdef CONFIG_FB_MSM_MDP40
552 } else if (term == MDP_DMA_S_TERM) {
553 mdp_pipe_ctrl(MDP_DMA_S_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
554 outpdw(MDP_BASE + 0x0010, 0x0); /* start DMA */
555 } else if (term == MDP_DMA_E_TERM) {
556 mdp_pipe_ctrl(MDP_DMA_E_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
557 outpdw(MDP_BASE + 0x0014, 0x0); /* start DMA */
558 } else if (term == MDP_OVERLAY0_TERM) {
559 mdp_pipe_ctrl(MDP_OVERLAY0_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
560 mdp_lut_enable();
561 outpdw(MDP_BASE + 0x0004, 0);
562 } else if (term == MDP_OVERLAY1_TERM) {
563 mdp_pipe_ctrl(MDP_OVERLAY1_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
564 mdp_lut_enable();
565 outpdw(MDP_BASE + 0x0008, 0);
566 }
567#else
568 } else if (term == MDP_DMA_S_TERM) {
569 mdp_pipe_ctrl(MDP_DMA_S_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
570 outpdw(MDP_BASE + 0x0048, 0x0); /* start DMA */
571 } else if (term == MDP_DMA_E_TERM) {
572 mdp_pipe_ctrl(MDP_DMA_E_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
573 outpdw(MDP_BASE + 0x004C, 0x0);
574 }
575#endif
576}
577static int mdp_clk_rate;
578static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
579static int pdev_list_cnt;
580
581static void mdp_pipe_ctrl_workqueue_handler(struct work_struct *work)
582{
583 mdp_pipe_ctrl(MDP_MASTER_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
584}
585void mdp_pipe_ctrl(MDP_BLOCK_TYPE block, MDP_BLOCK_POWER_STATE state,
586 boolean isr)
587{
588 boolean mdp_all_blocks_off = TRUE;
589 int i;
590 unsigned long flag;
591 struct msm_fb_panel_data *pdata;
592
593 /*
594 * It is assumed that if isr = TRUE then start = OFF
595 * if start = ON when isr = TRUE it could happen that the usercontext
596 * could turn off the clocks while the interrupt is updating the
597 * power to ON
598 */
599 WARN_ON(isr == TRUE && state == MDP_BLOCK_POWER_ON);
600
601 spin_lock_irqsave(&mdp_spin_lock, flag);
602 if (MDP_BLOCK_POWER_ON == state) {
603 atomic_inc(&mdp_block_power_cnt[block]);
604
605 if (MDP_DMA2_BLOCK == block)
606 mdp_in_processing = TRUE;
607 } else {
608 atomic_dec(&mdp_block_power_cnt[block]);
609
610 if (atomic_read(&mdp_block_power_cnt[block]) < 0) {
611 /*
612 * Master has to serve a request to power off MDP always
613 * It also has a timer to power off. So, in case of
614 * timer expires first and DMA2 finishes later,
615 * master has to power off two times
616 * There shouldn't be multiple power-off request for
617 * other blocks
618 */
619 if (block != MDP_MASTER_BLOCK) {
620 MSM_FB_INFO("mdp_block_power_cnt[block=%d] \
621 multiple power-off request\n", block);
622 }
623 atomic_set(&mdp_block_power_cnt[block], 0);
624 }
625
626 if (MDP_DMA2_BLOCK == block)
627 mdp_in_processing = FALSE;
628 }
629 spin_unlock_irqrestore(&mdp_spin_lock, flag);
630
631 /*
632 * If it's in isr, we send our request to workqueue.
633 * Otherwise, processing happens in the current context
634 */
635 if (isr) {
636 if (mdp_current_clk_on) {
637 /* checking all blocks power state */
638 for (i = 0; i < MDP_MAX_BLOCK; i++) {
639 if (atomic_read(&mdp_block_power_cnt[i]) > 0) {
640 mdp_all_blocks_off = FALSE;
641 break;
642 }
643 }
644
645 if (mdp_all_blocks_off) {
646 /* send workqueue to turn off mdp power */
647 queue_delayed_work(mdp_pipe_ctrl_wq,
648 &mdp_pipe_ctrl_worker,
649 mdp_timer_duration);
650 }
651 }
652 } else {
653 down(&mdp_pipe_ctrl_mutex);
654 /* checking all blocks power state */
655 for (i = 0; i < MDP_MAX_BLOCK; i++) {
656 if (atomic_read(&mdp_block_power_cnt[i]) > 0) {
657 mdp_all_blocks_off = FALSE;
658 break;
659 }
660 }
661
662 /*
663 * find out whether a delayable work item is currently
664 * pending
665 */
666
667 if (delayed_work_pending(&mdp_pipe_ctrl_worker)) {
668 /*
669 * try to cancel the current work if it fails to
670 * stop (which means del_timer can't delete it
671 * from the list, it's about to expire and run),
672 * we have to let it run. queue_delayed_work won't
673 * accept the next job which is same as
674 * queue_delayed_work(mdp_timer_duration = 0)
675 */
676 cancel_delayed_work(&mdp_pipe_ctrl_worker);
677 }
678
679 if ((mdp_all_blocks_off) && (mdp_current_clk_on)) {
680 mutex_lock(&mdp_suspend_mutex);
681 if (block == MDP_MASTER_BLOCK || mdp_suspended) {
682 mdp_current_clk_on = FALSE;
683 mb();
684 /* turn off MDP clks */
685 mdp_vsync_clk_disable();
686 for (i = 0; i < pdev_list_cnt; i++) {
687 pdata = (struct msm_fb_panel_data *)
688 pdev_list[i]->dev.platform_data;
689 if (pdata && pdata->clk_func)
690 pdata->clk_func(0);
691 }
692 if (mdp_clk != NULL) {
693 mdp_clk_rate = clk_get_rate(mdp_clk);
694 clk_disable(mdp_clk);
695 if (mdp_hw_revision <=
696 MDP4_REVISION_V2_1 &&
697 mdp_clk_rate > 122880000) {
698 clk_set_rate(mdp_clk,
699 122880000);
700 }
701 MSM_FB_DEBUG("MDP CLK OFF\n");
702 }
703 if (mdp_pclk != NULL) {
704 clk_disable(mdp_pclk);
705 MSM_FB_DEBUG("MDP PCLK OFF\n");
706 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700707 if (mdp_lut_clk != NULL)
708 clk_disable(mdp_lut_clk);
709 } else {
710 /* send workqueue to turn off mdp power */
711 queue_delayed_work(mdp_pipe_ctrl_wq,
712 &mdp_pipe_ctrl_worker,
713 mdp_timer_duration);
714 }
715 mutex_unlock(&mdp_suspend_mutex);
716 } else if ((!mdp_all_blocks_off) && (!mdp_current_clk_on)) {
717 mdp_current_clk_on = TRUE;
718 /* turn on MDP clks */
719 for (i = 0; i < pdev_list_cnt; i++) {
720 pdata = (struct msm_fb_panel_data *)
721 pdev_list[i]->dev.platform_data;
722 if (pdata && pdata->clk_func)
723 pdata->clk_func(1);
724 }
725 if (mdp_clk != NULL) {
726 if (mdp_hw_revision <=
727 MDP4_REVISION_V2_1 &&
728 mdp_clk_rate > 122880000) {
729 clk_set_rate(mdp_clk,
730 mdp_clk_rate);
731 }
732 clk_enable(mdp_clk);
733 MSM_FB_DEBUG("MDP CLK ON\n");
734 }
735 if (mdp_pclk != NULL) {
736 clk_enable(mdp_pclk);
737 MSM_FB_DEBUG("MDP PCLK ON\n");
738 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700739 if (mdp_lut_clk != NULL)
740 clk_enable(mdp_lut_clk);
741 mdp_vsync_clk_enable();
742 }
743 up(&mdp_pipe_ctrl_mutex);
744 }
745}
746
747#ifndef CONFIG_FB_MSM_MDP40
748irqreturn_t mdp_isr(int irq, void *ptr)
749{
750 uint32 mdp_interrupt = 0;
751 struct mdp_dma_data *dma;
752
753 mdp_is_in_isr = TRUE;
754 do {
755 mdp_interrupt = inp32(MDP_INTR_STATUS);
756 outp32(MDP_INTR_CLEAR, mdp_interrupt);
757
758 mdp_interrupt &= mdp_intr_mask;
759
760 if (mdp_interrupt & TV_ENC_UNDERRUN) {
761 mdp_interrupt &= ~(TV_ENC_UNDERRUN);
762 mdp_tv_underflow_cnt++;
763 }
764
765 if (!mdp_interrupt)
766 break;
767
768 /* DMA3 TV-Out Start */
769 if (mdp_interrupt & TV_OUT_DMA3_START) {
770 /* let's disable TV out interrupt */
771 mdp_intr_mask &= ~TV_OUT_DMA3_START;
772 outp32(MDP_INTR_ENABLE, mdp_intr_mask);
773
774 dma = &dma3_data;
775 if (dma->waiting) {
776 dma->waiting = FALSE;
777 complete(&dma->comp);
778 }
779 }
780#ifndef CONFIG_FB_MSM_MDP22
781 if (mdp_interrupt & MDP_HIST_DONE) {
782 outp32(MDP_BASE + 0x94018, 0x3);
783 outp32(MDP_INTR_CLEAR, MDP_HIST_DONE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700784 complete(&mdp_hist_comp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700785 }
786
787 /* LCDC UnderFlow */
788 if (mdp_interrupt & LCDC_UNDERFLOW) {
789 mdp_lcdc_underflow_cnt++;
790 /*when underflow happens HW resets all the histogram
791 registers that were set before so restore them back
792 to normal.*/
793 MDP_OUTP(MDP_BASE + 0x94010, 1);
794 MDP_OUTP(MDP_BASE + 0x9401c, 2);
795 if (mdp_is_hist_start == TRUE) {
796 MDP_OUTP(MDP_BASE + 0x94004,
Ravishangar Kalyanam8fef09a2011-08-09 17:36:23 -0700797 mdp_hist_frame_cnt);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700798 MDP_OUTP(MDP_BASE + 0x94000, 1);
799 }
800 }
801 /* LCDC Frame Start */
802 if (mdp_interrupt & LCDC_FRAME_START) {
803 /* let's disable LCDC interrupt */
804 mdp_intr_mask &= ~LCDC_FRAME_START;
805 outp32(MDP_INTR_ENABLE, mdp_intr_mask);
806
807 dma = &dma2_data;
808 if (dma->waiting) {
809 dma->waiting = FALSE;
810 complete(&dma->comp);
811 }
812 }
813
814 /* DMA2 LCD-Out Complete */
815 if (mdp_interrupt & MDP_DMA_S_DONE) {
816 dma = &dma_s_data;
817 dma->busy = FALSE;
818 mdp_pipe_ctrl(MDP_DMA_S_BLOCK, MDP_BLOCK_POWER_OFF,
819 TRUE);
820 complete(&dma->comp);
821 }
822 /* DMA_E LCD-Out Complete */
823 if (mdp_interrupt & MDP_DMA_E_DONE) {
824 dma = &dma_s_data;
825 dma->busy = FALSE;
826 mdp_pipe_ctrl(MDP_DMA_E_BLOCK, MDP_BLOCK_POWER_OFF,
827 TRUE);
828 complete(&dma->comp);
829 }
830
831#endif
832
833 /* DMA2 LCD-Out Complete */
834 if (mdp_interrupt & MDP_DMA_P_DONE) {
835 struct timeval now;
836
837 mdp_dma2_last_update_time = ktime_sub(ktime_get_real(),
838 mdp_dma2_last_update_time);
839 if (mdp_debug[MDP_DMA2_BLOCK]) {
840 jiffies_to_timeval(jiffies, &now);
841 mdp_dma2_timeval.tv_usec =
842 now.tv_usec - mdp_dma2_timeval.tv_usec;
843 }
844#ifndef CONFIG_FB_MSM_MDP303
845 dma = &dma2_data;
846 dma->busy = FALSE;
847 mdp_pipe_ctrl(MDP_DMA2_BLOCK, MDP_BLOCK_POWER_OFF,
848 TRUE);
849 complete(&dma->comp);
850#else
851 if (mdp_prim_panel_type == MIPI_CMD_PANEL) {
852 dma = &dma2_data;
853 dma->busy = FALSE;
854 mdp_pipe_ctrl(MDP_DMA2_BLOCK,
855 MDP_BLOCK_POWER_OFF, TRUE);
856 complete(&dma->comp);
857 }
858#endif
859 }
860 /* PPP Complete */
861 if (mdp_interrupt & MDP_PPP_DONE) {
862#ifdef CONFIG_FB_MSM_MDP31
863 MDP_OUTP(MDP_BASE + 0x00100, 0xFFFF);
864#endif
865 mdp_pipe_ctrl(MDP_PPP_BLOCK, MDP_BLOCK_POWER_OFF, TRUE);
866 if (mdp_ppp_waiting) {
867 mdp_ppp_waiting = FALSE;
868 complete(&mdp_ppp_comp);
869 }
870 }
871 } while (1);
872
873 mdp_is_in_isr = FALSE;
874
Pavel Machekd480ace2009-09-22 16:47:03 -0700875 return IRQ_HANDLED;
876}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700877#endif
Pavel Machekd480ace2009-09-22 16:47:03 -0700878
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700879static void mdp_drv_init(void)
Pavel Machekd480ace2009-09-22 16:47:03 -0700880{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700881 int i;
Pavel Machekd480ace2009-09-22 16:47:03 -0700882
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700883 for (i = 0; i < MDP_MAX_BLOCK; i++) {
884 mdp_debug[i] = 0;
Pavel Machekd480ace2009-09-22 16:47:03 -0700885 }
886
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700887 /* initialize spin lock and workqueue */
888 spin_lock_init(&mdp_spin_lock);
889 mdp_dma_wq = create_singlethread_workqueue("mdp_dma_wq");
890 mdp_vsync_wq = create_singlethread_workqueue("mdp_vsync_wq");
891 mdp_pipe_ctrl_wq = create_singlethread_workqueue("mdp_pipe_ctrl_wq");
892 INIT_DELAYED_WORK(&mdp_pipe_ctrl_worker,
893 mdp_pipe_ctrl_workqueue_handler);
Pavel Machekd480ace2009-09-22 16:47:03 -0700894
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700895 /* initialize semaphore */
896 init_completion(&mdp_ppp_comp);
897 sema_init(&mdp_ppp_mutex, 1);
898 sema_init(&mdp_pipe_ctrl_mutex, 1);
Pavel Machekd480ace2009-09-22 16:47:03 -0700899
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700900 dma2_data.busy = FALSE;
901 dma2_data.dmap_busy = FALSE;
902 dma2_data.waiting = FALSE;
903 init_completion(&dma2_data.comp);
904 init_completion(&dma2_data.dmap_comp);
905 sema_init(&dma2_data.mutex, 1);
906 mutex_init(&dma2_data.ov_mutex);
Pavel Machekd480ace2009-09-22 16:47:03 -0700907
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700908 dma3_data.busy = FALSE;
909 dma3_data.waiting = FALSE;
910 init_completion(&dma3_data.comp);
911 sema_init(&dma3_data.mutex, 1);
Pavel Machekd480ace2009-09-22 16:47:03 -0700912
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700913 dma_s_data.busy = FALSE;
914 dma_s_data.waiting = FALSE;
915 init_completion(&dma_s_data.comp);
916 sema_init(&dma_s_data.mutex, 1);
Pavel Machekd480ace2009-09-22 16:47:03 -0700917
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700918#ifndef CONFIG_FB_MSM_MDP303
919 dma_e_data.busy = FALSE;
920 dma_e_data.waiting = FALSE;
921 init_completion(&dma_e_data.comp);
922 mutex_init(&dma_e_data.ov_mutex);
923#endif
Pavel Machekd480ace2009-09-22 16:47:03 -0700924
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700925#ifndef CONFIG_FB_MSM_MDP22
926 init_completion(&mdp_hist_comp);
927#endif
Pavel Machekd480ace2009-09-22 16:47:03 -0700928
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700929 /* initializing mdp power block counter to 0 */
930 for (i = 0; i < MDP_MAX_BLOCK; i++) {
931 atomic_set(&mdp_block_power_cnt[i], 0);
Pavel Machekd480ace2009-09-22 16:47:03 -0700932 }
933
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700934#ifdef MSM_FB_ENABLE_DBGFS
935 {
936 struct dentry *root;
937 char sub_name[] = "mdp";
Pavel Machekd480ace2009-09-22 16:47:03 -0700938
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700939 root = msm_fb_get_debugfs_root();
940 if (root != NULL) {
941 mdp_dir = debugfs_create_dir(sub_name, root);
942
943 if (mdp_dir) {
944 msm_fb_debugfs_file_create(mdp_dir,
945 "dma2_update_time_in_usec",
946 (u32 *) &mdp_dma2_update_time_in_usec);
947 msm_fb_debugfs_file_create(mdp_dir,
948 "vs_rdcnt_slow",
949 (u32 *) &mdp_lcd_rd_cnt_offset_slow);
950 msm_fb_debugfs_file_create(mdp_dir,
951 "vs_rdcnt_fast",
952 (u32 *) &mdp_lcd_rd_cnt_offset_fast);
953 msm_fb_debugfs_file_create(mdp_dir,
954 "mdp_usec_diff_threshold",
955 (u32 *) &mdp_usec_diff_threshold);
956 msm_fb_debugfs_file_create(mdp_dir,
957 "mdp_current_clk_on",
958 (u32 *) &mdp_current_clk_on);
959#ifdef CONFIG_FB_MSM_LCDC
960 msm_fb_debugfs_file_create(mdp_dir,
961 "lcdc_start_x",
962 (u32 *) &first_pixel_start_x);
963 msm_fb_debugfs_file_create(mdp_dir,
964 "lcdc_start_y",
965 (u32 *) &first_pixel_start_y);
966#endif
967 }
Pavel Machekd480ace2009-09-22 16:47:03 -0700968 }
Pavel Machekd480ace2009-09-22 16:47:03 -0700969 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700970#endif
971}
972
973static int mdp_probe(struct platform_device *pdev);
974static int mdp_remove(struct platform_device *pdev);
975
976static int mdp_runtime_suspend(struct device *dev)
977{
978 dev_dbg(dev, "pm_runtime: suspending...\n");
Pavel Machekd480ace2009-09-22 16:47:03 -0700979 return 0;
Pavel Machekd480ace2009-09-22 16:47:03 -0700980}
981
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700982static int mdp_runtime_resume(struct device *dev)
Pavel Machekd480ace2009-09-22 16:47:03 -0700983{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700984 dev_dbg(dev, "pm_runtime: resuming...\n");
Pavel Machekd480ace2009-09-22 16:47:03 -0700985 return 0;
Pavel Machekd480ace2009-09-22 16:47:03 -0700986}
987
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700988static struct dev_pm_ops mdp_dev_pm_ops = {
989 .runtime_suspend = mdp_runtime_suspend,
990 .runtime_resume = mdp_runtime_resume,
Pavel Machekd480ace2009-09-22 16:47:03 -0700991};
992
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700993
994static struct platform_driver mdp_driver = {
995 .probe = mdp_probe,
996 .remove = mdp_remove,
997#ifndef CONFIG_HAS_EARLYSUSPEND
998 .suspend = mdp_suspend,
999 .resume = NULL,
1000#endif
1001 .shutdown = NULL,
1002 .driver = {
1003 /*
1004 * Driver name must match the device name added in
1005 * platform.c.
1006 */
1007 .name = "mdp",
1008 .pm = &mdp_dev_pm_ops,
1009 },
1010};
1011
1012static int mdp_off(struct platform_device *pdev)
Pavel Machekd480ace2009-09-22 16:47:03 -07001013{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001014 int ret = 0;
1015 mdp_histogram_ctrl(FALSE);
1016
1017 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
1018 ret = panel_next_off(pdev);
1019 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
1020
1021 return ret;
Pavel Machekd480ace2009-09-22 16:47:03 -07001022}
1023
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001024static int mdp_on(struct platform_device *pdev)
1025{
1026 int ret = 0;
Ravishangar Kalyanam419051b2011-08-31 19:07:53 -07001027
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001028#ifdef CONFIG_FB_MSM_MDP40
Ravishangar Kalyanam419051b2011-08-31 19:07:53 -07001029 struct msm_fb_data_type *mfd;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001030 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
1031 if (is_mdp4_hw_reset()) {
Ravishangar Kalyanam419051b2011-08-31 19:07:53 -07001032 mfd = platform_get_drvdata(pdev);
1033 mdp_vsync_cfg_regs(mfd, FALSE);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001034 mdp4_hw_init();
1035 outpdw(MDP_BASE + 0x0038, mdp4_display_intf);
1036 }
1037 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
1038#endif
1039 mdp_histogram_ctrl(TRUE);
1040
1041 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
1042 ret = panel_next_on(pdev);
1043 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
1044 return ret;
1045}
1046
1047static int mdp_resource_initialized;
1048static struct msm_panel_common_pdata *mdp_pdata;
1049
1050uint32 mdp_hw_revision;
1051
1052/*
1053 * mdp_hw_revision:
1054 * 0 == V1
1055 * 1 == V2
1056 * 2 == V2.1
1057 *
1058 */
1059void mdp_hw_version(void)
1060{
1061 char *cp;
1062 uint32 *hp;
1063
1064 if (mdp_pdata == NULL)
1065 return;
1066
1067 mdp_hw_revision = MDP4_REVISION_NONE;
1068 if (mdp_pdata->hw_revision_addr == 0)
1069 return;
1070
1071 /* tlmmgpio2 shadow */
1072 cp = (char *)ioremap(mdp_pdata->hw_revision_addr, 0x16);
1073
1074 if (cp == NULL)
1075 return;
1076
1077 hp = (uint32 *)cp; /* HW_REVISION_NUMBER */
1078 mdp_hw_revision = *hp;
1079 iounmap(cp);
1080
1081 mdp_hw_revision >>= 28; /* bit 31:28 */
1082 mdp_hw_revision &= 0x0f;
1083
1084 MSM_FB_DEBUG("%s: mdp_hw_revision=%x\n",
1085 __func__, mdp_hw_revision);
1086}
1087
kuogee hsieh5a7f32c2011-08-31 17:51:34 -07001088int mdp4_writeback_offset(void)
1089{
1090 int off = 0;
1091
1092 if (mdp_pdata->writeback_offset)
1093 off = mdp_pdata->writeback_offset();
1094
1095 pr_debug("%s: writeback_offset=%d %x\n", __func__, off, off);
1096
1097 return off;
1098}
1099
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001100#ifdef CONFIG_FB_MSM_MDP40
1101static void configure_mdp_core_clk_table(uint32 min_clk_rate)
1102{
1103 uint8 count;
1104 uint32 current_rate;
1105 if (mdp_clk && mdp_pdata
1106 && mdp_pdata->mdp_core_clk_table) {
1107 if (clk_set_min_rate(mdp_clk,
1108 min_clk_rate) < 0)
1109 printk(KERN_ERR "%s: clk_set_min_rate failed\n",
1110 __func__);
1111 else {
1112 count = 0;
1113 current_rate = clk_get_rate(mdp_clk);
1114 while (count < mdp_pdata->num_mdp_clk) {
1115 if (mdp_pdata->mdp_core_clk_table[count]
1116 < current_rate) {
1117 mdp_pdata->
1118 mdp_core_clk_table[count] =
1119 current_rate;
1120 }
1121 count++;
1122 }
1123 }
1124 }
1125}
1126#endif
1127
1128#ifdef CONFIG_MSM_BUS_SCALING
1129static uint32_t mdp_bus_scale_handle;
1130int mdp_bus_scale_update_request(uint32_t index)
1131{
1132 if (!mdp_pdata && (!mdp_pdata->mdp_bus_scale_table
1133 || index > (mdp_pdata->mdp_bus_scale_table->num_usecases - 1))) {
1134 printk(KERN_ERR "%s invalid table or index\n", __func__);
1135 return -EINVAL;
1136 }
1137 if (mdp_bus_scale_handle < 1) {
1138 printk(KERN_ERR "%s invalid bus handle\n", __func__);
1139 return -EINVAL;
1140 }
1141 return msm_bus_scale_client_update_request(mdp_bus_scale_handle,
1142 index);
1143}
1144#endif
1145DEFINE_MUTEX(mdp_clk_lock);
1146int mdp_set_core_clk(uint16 perf_level)
1147{
1148 int ret = -EINVAL;
1149 if (mdp_clk && mdp_pdata
1150 && mdp_pdata->mdp_core_clk_table) {
1151 if (perf_level > mdp_pdata->num_mdp_clk)
1152 printk(KERN_ERR "%s invalid perf level\n", __func__);
1153 else {
1154 mutex_lock(&mdp_clk_lock);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001155 ret = clk_set_rate(mdp_clk,
1156 mdp_pdata->
1157 mdp_core_clk_table[mdp_pdata->num_mdp_clk
1158 - perf_level]);
1159 mutex_unlock(&mdp_clk_lock);
1160 if (ret) {
1161 printk(KERN_ERR "%s unable to set mdp_core_clk rate\n",
1162 __func__);
1163 }
1164 }
1165 }
1166 return ret;
1167}
1168
1169unsigned long mdp_get_core_clk(void)
1170{
1171 unsigned long clk_rate = 0;
1172 if (mdp_clk) {
1173 mutex_lock(&mdp_clk_lock);
1174 clk_rate = clk_get_rate(mdp_clk);
1175 mutex_unlock(&mdp_clk_lock);
1176 }
1177
1178 return clk_rate;
1179}
1180
1181unsigned long mdp_perf_level2clk_rate(uint32 perf_level)
1182{
1183 unsigned long clk_rate = 0;
1184
1185 if (mdp_pdata && mdp_pdata->mdp_core_clk_table) {
1186 if (perf_level > mdp_pdata->num_mdp_clk) {
1187 printk(KERN_ERR "%s invalid perf level\n", __func__);
1188 clk_rate = mdp_get_core_clk();
1189 } else {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001190 clk_rate = mdp_pdata->
1191 mdp_core_clk_table[mdp_pdata->num_mdp_clk
1192 - perf_level];
1193 }
1194 } else
1195 clk_rate = mdp_get_core_clk();
1196
1197 return clk_rate;
1198}
1199
1200static int mdp_irq_clk_setup(void)
1201{
1202 int ret;
1203
1204#ifdef CONFIG_FB_MSM_MDP40
1205 ret = request_irq(mdp_irq, mdp4_isr, IRQF_DISABLED, "MDP", 0);
1206#else
1207 ret = request_irq(mdp_irq, mdp_isr, IRQF_DISABLED, "MDP", 0);
1208#endif
1209 if (ret) {
1210 printk(KERN_ERR "mdp request_irq() failed!\n");
1211 return ret;
1212 }
1213 disable_irq(mdp_irq);
1214
1215 footswitch = regulator_get(NULL, "fs_mdp");
1216 if (IS_ERR(footswitch))
1217 footswitch = NULL;
Ravishangar Kalyanam419051b2011-08-31 19:07:53 -07001218 else
1219 regulator_enable(footswitch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001220
1221 mdp_clk = clk_get(NULL, "mdp_clk");
1222 if (IS_ERR(mdp_clk)) {
1223 ret = PTR_ERR(mdp_clk);
1224 printk(KERN_ERR "can't get mdp_clk error:%d!\n", ret);
1225 free_irq(mdp_irq, 0);
1226 return ret;
1227 }
1228
1229 mdp_pclk = clk_get(NULL, "mdp_pclk");
1230 if (IS_ERR(mdp_pclk))
1231 mdp_pclk = NULL;
1232
1233 if (mdp_rev == MDP_REV_42) {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001234 mdp_lut_clk = clk_get(NULL, "lut_mdp");
1235 if (IS_ERR(mdp_lut_clk)) {
1236 ret = PTR_ERR(mdp_lut_clk);
1237 pr_err("can't get mdp_clk error:%d!\n", ret);
1238 clk_put(mdp_clk);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001239 free_irq(mdp_irq, 0);
1240 return ret;
1241 }
1242 } else {
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001243 mdp_lut_clk = NULL;
1244 }
1245
1246#ifdef CONFIG_FB_MSM_MDP40
1247 /*
1248 * mdp_clk should greater than mdp_pclk always
1249 */
1250 if (mdp_pdata && mdp_pdata->mdp_core_clk_rate) {
1251 mutex_lock(&mdp_clk_lock);
1252 clk_set_rate(mdp_clk, mdp_pdata->mdp_core_clk_rate);
1253 if (mdp_lut_clk != NULL)
1254 clk_set_rate(mdp_lut_clk, mdp_pdata->mdp_core_clk_rate);
1255 mutex_unlock(&mdp_clk_lock);
1256 }
1257 MSM_FB_DEBUG("mdp_clk: mdp_clk=%d\n", (int)clk_get_rate(mdp_clk));
1258#endif
1259 return 0;
1260}
1261
1262static int mdp_probe(struct platform_device *pdev)
1263{
1264 struct platform_device *msm_fb_dev = NULL;
1265 struct msm_fb_data_type *mfd;
1266 struct msm_fb_panel_data *pdata = NULL;
1267 int rc;
1268 resource_size_t size ;
1269#ifdef CONFIG_FB_MSM_MDP40
1270 int intf, if_no;
1271#else
1272 unsigned long flag;
1273#endif
1274#if defined(CONFIG_FB_MSM_MIPI_DSI) && defined(CONFIG_FB_MSM_MDP40)
1275 struct mipi_panel_info *mipi;
1276#endif
1277
1278 if ((pdev->id == 0) && (pdev->num_resources > 0)) {
1279 mdp_pdata = pdev->dev.platform_data;
1280
1281 size = resource_size(&pdev->resource[0]);
1282 msm_mdp_base = ioremap(pdev->resource[0].start, size);
1283
1284 MSM_FB_DEBUG("MDP HW Base phy_Address = 0x%x virt = 0x%x\n",
1285 (int)pdev->resource[0].start, (int)msm_mdp_base);
1286
1287 if (unlikely(!msm_mdp_base))
1288 return -ENOMEM;
1289
1290 mdp_irq = platform_get_irq(pdev, 0);
1291 if (mdp_irq < 0) {
1292 pr_err("mdp: can not get mdp irq\n");
1293 return -ENOMEM;
1294 }
1295
1296 mdp_rev = mdp_pdata->mdp_rev;
1297 rc = mdp_irq_clk_setup();
1298
1299 if (rc)
1300 return rc;
1301
1302 mdp_hw_version();
1303
1304 /* initializing mdp hw */
1305#ifdef CONFIG_FB_MSM_MDP40
1306 mdp4_hw_init();
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001307#else
1308 mdp_hw_init();
1309#endif
1310
1311#ifdef CONFIG_FB_MSM_OVERLAY
1312 mdp_hw_cursor_init();
1313#endif
1314
1315 mdp_resource_initialized = 1;
1316 return 0;
1317 }
1318
1319 if (!mdp_resource_initialized)
1320 return -EPERM;
1321
1322 mfd = platform_get_drvdata(pdev);
1323
1324 if (!mfd)
1325 return -ENODEV;
1326
1327 if (mfd->key != MFD_KEY)
1328 return -EINVAL;
1329
1330 if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
1331 return -ENOMEM;
1332
1333 msm_fb_dev = platform_device_alloc("msm_fb", pdev->id);
1334 if (!msm_fb_dev)
1335 return -ENOMEM;
1336
1337 /* link to the latest pdev */
1338 mfd->pdev = msm_fb_dev;
1339
1340 /* add panel data */
1341 if (platform_device_add_data
1342 (msm_fb_dev, pdev->dev.platform_data,
1343 sizeof(struct msm_fb_panel_data))) {
1344 printk(KERN_ERR "mdp_probe: platform_device_add_data failed!\n");
1345 rc = -ENOMEM;
1346 goto mdp_probe_err;
1347 }
1348 /* data chain */
1349 pdata = msm_fb_dev->dev.platform_data;
1350 pdata->on = mdp_on;
1351 pdata->off = mdp_off;
1352 pdata->next = pdev;
1353
1354 mdp_prim_panel_type = mfd->panel.type;
1355 switch (mfd->panel.type) {
1356 case EXT_MDDI_PANEL:
1357 case MDDI_PANEL:
1358 case EBI2_PANEL:
1359 INIT_WORK(&mfd->dma_update_worker,
1360 mdp_lcd_update_workqueue_handler);
1361 INIT_WORK(&mfd->vsync_resync_worker,
1362 mdp_vsync_resync_workqueue_handler);
1363 mfd->hw_refresh = FALSE;
1364
1365 if (mfd->panel.type == EXT_MDDI_PANEL) {
1366 /* 15 fps -> 66 msec */
1367 mfd->refresh_timer_duration = (66 * HZ / 1000);
1368 } else {
1369 /* 24 fps -> 42 msec */
1370 mfd->refresh_timer_duration = (42 * HZ / 1000);
1371 }
1372
1373#ifdef CONFIG_FB_MSM_MDP22
1374 mfd->dma_fnc = mdp_dma2_update;
1375 mfd->dma = &dma2_data;
1376#else
1377 if (mfd->panel_info.pdest == DISPLAY_1) {
1378#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDDI)
1379 mfd->dma_fnc = mdp4_mddi_overlay;
1380 mfd->cursor_update = mdp4_mddi_overlay_cursor;
1381#else
1382 mfd->dma_fnc = mdp_dma2_update;
1383#endif
1384 mfd->dma = &dma2_data;
1385 mfd->lut_update = mdp_lut_update_nonlcdc;
1386 mfd->do_histogram = mdp_do_histogram;
1387 } else {
1388 mfd->dma_fnc = mdp_dma_s_update;
1389 mfd->dma = &dma_s_data;
1390 }
1391#endif
1392 if (mdp_pdata)
1393 mfd->vsync_gpio = mdp_pdata->gpio;
1394 else
1395 mfd->vsync_gpio = -1;
1396
1397#ifdef CONFIG_FB_MSM_MDP40
1398 if (mfd->panel.type == EBI2_PANEL)
1399 intf = EBI2_INTF;
1400 else
1401 intf = MDDI_INTF;
1402
1403 if (mfd->panel_info.pdest == DISPLAY_1)
1404 if_no = PRIMARY_INTF_SEL;
1405 else
1406 if_no = SECONDARY_INTF_SEL;
1407
1408 mdp4_display_intf_sel(if_no, intf);
1409#endif
1410 mdp_config_vsync(mfd);
1411 break;
1412
1413#ifdef CONFIG_FB_MSM_MIPI_DSI
1414 case MIPI_VIDEO_PANEL:
1415#ifndef CONFIG_FB_MSM_MDP303
1416 pdata->on = mdp4_dsi_video_on;
1417 pdata->off = mdp4_dsi_video_off;
1418 mfd->hw_refresh = TRUE;
1419 mfd->dma_fnc = mdp4_dsi_video_overlay;
Ravishangar Kalyaname7833e22011-07-22 16:20:19 -07001420 mfd->lut_update = mdp_lut_update_lcdc;
1421 mfd->do_histogram = mdp_do_histogram;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001422 if (mfd->panel_info.pdest == DISPLAY_1) {
1423 if_no = PRIMARY_INTF_SEL;
1424 mfd->dma = &dma2_data;
1425 } else {
1426 if_no = EXTERNAL_INTF_SEL;
1427 mfd->dma = &dma_e_data;
1428 }
1429 mdp4_display_intf_sel(if_no, DSI_VIDEO_INTF);
1430#else
1431 pdata->on = mdp_dsi_video_on;
1432 pdata->off = mdp_dsi_video_off;
1433 mfd->hw_refresh = TRUE;
1434 mfd->dma_fnc = mdp_dsi_video_update;
1435 mfd->do_histogram = mdp_do_histogram;
1436 if (mfd->panel_info.pdest == DISPLAY_1)
1437 mfd->dma = &dma2_data;
1438 else {
1439 printk(KERN_ERR "Invalid Selection of destination panel\n");
1440 rc = -ENODEV;
1441 goto mdp_probe_err;
1442 }
1443
1444#endif
Adrian Salido-Morenod1b9d7a2011-10-14 18:18:51 -07001445 if (mdp_rev >= MDP_REV_40)
1446 mfd->cursor_update = mdp_hw_cursor_sync_update;
1447 else
1448 mfd->cursor_update = mdp_hw_cursor_update;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001449 break;
1450
1451 case MIPI_CMD_PANEL:
1452#ifndef CONFIG_FB_MSM_MDP303
1453 mfd->dma_fnc = mdp4_dsi_cmd_overlay;
1454#ifdef CONFIG_FB_MSM_MDP40
1455 mipi = &mfd->panel_info.mipi;
1456 configure_mdp_core_clk_table((mipi->dsi_pclk_rate) * 3 / 2);
1457#endif
1458 if (mfd->panel_info.pdest == DISPLAY_1) {
1459 if_no = PRIMARY_INTF_SEL;
1460 mfd->dma = &dma2_data;
1461 } else {
1462 if_no = SECONDARY_INTF_SEL;
1463 mfd->dma = &dma_s_data;
1464 }
Carl Vanderlip18f63082011-07-22 12:32:33 -07001465 mfd->lut_update = mdp_lut_update_nonlcdc;
1466 mfd->do_histogram = mdp_do_histogram;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001467 mdp4_display_intf_sel(if_no, DSI_CMD_INTF);
1468#else
1469 mfd->dma_fnc = mdp_dma2_update;
1470 mfd->do_histogram = mdp_do_histogram;
1471 if (mfd->panel_info.pdest == DISPLAY_1)
1472 mfd->dma = &dma2_data;
1473 else {
1474 printk(KERN_ERR "Invalid Selection of destination panel\n");
1475 rc = -ENODEV;
1476 goto mdp_probe_err;
1477 }
1478#endif
1479 mdp_config_vsync(mfd);
1480 break;
1481#endif
1482
1483#ifdef CONFIG_FB_MSM_DTV
1484 case DTV_PANEL:
1485 pdata->on = mdp4_dtv_on;
1486 pdata->off = mdp4_dtv_off;
1487 mfd->hw_refresh = TRUE;
1488 mfd->cursor_update = mdp_hw_cursor_update;
1489 mfd->dma_fnc = mdp4_dtv_overlay;
1490 mfd->dma = &dma_e_data;
1491 mdp4_display_intf_sel(EXTERNAL_INTF_SEL, DTV_INTF);
1492 break;
1493#endif
1494 case HDMI_PANEL:
1495 case LCDC_PANEL:
1496 pdata->on = mdp_lcdc_on;
1497 pdata->off = mdp_lcdc_off;
1498 mfd->hw_refresh = TRUE;
1499#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_MDP40)
1500 mfd->cursor_update = mdp_hw_cursor_sync_update;
1501#else
1502 mfd->cursor_update = mdp_hw_cursor_update;
1503#endif
1504#ifndef CONFIG_FB_MSM_MDP22
1505 mfd->lut_update = mdp_lut_update_lcdc;
1506 mfd->do_histogram = mdp_do_histogram;
1507#endif
1508#ifdef CONFIG_FB_MSM_OVERLAY
1509 mfd->dma_fnc = mdp4_lcdc_overlay;
1510#else
1511 mfd->dma_fnc = mdp_lcdc_update;
1512#endif
1513
1514#ifdef CONFIG_FB_MSM_MDP40
1515 configure_mdp_core_clk_table((mfd->panel_info.clk_rate)
1516 * 23 / 20);
1517 if (mfd->panel.type == HDMI_PANEL) {
1518 mfd->dma = &dma_e_data;
1519 mdp4_display_intf_sel(EXTERNAL_INTF_SEL, LCDC_RGB_INTF);
1520 } else {
1521 mfd->dma = &dma2_data;
1522 mdp4_display_intf_sel(PRIMARY_INTF_SEL, LCDC_RGB_INTF);
1523 }
1524#else
1525 mfd->dma = &dma2_data;
1526 spin_lock_irqsave(&mdp_spin_lock, flag);
1527 mdp_intr_mask &= ~MDP_DMA_P_DONE;
1528 outp32(MDP_INTR_ENABLE, mdp_intr_mask);
1529 spin_unlock_irqrestore(&mdp_spin_lock, flag);
1530#endif
1531 break;
1532
1533 case TV_PANEL:
1534#if defined(CONFIG_FB_MSM_OVERLAY) && defined(CONFIG_FB_MSM_TVOUT)
1535 pdata->on = mdp4_atv_on;
1536 pdata->off = mdp4_atv_off;
1537 mfd->dma_fnc = mdp4_atv_overlay;
1538 mfd->dma = &dma_e_data;
1539 mdp4_display_intf_sel(EXTERNAL_INTF_SEL, TV_INTF);
1540#else
1541 pdata->on = mdp_dma3_on;
1542 pdata->off = mdp_dma3_off;
1543 mfd->hw_refresh = TRUE;
1544 mfd->dma_fnc = mdp_dma3_update;
1545 mfd->dma = &dma3_data;
1546#endif
1547 break;
1548
1549 default:
1550 printk(KERN_ERR "mdp_probe: unknown device type!\n");
1551 rc = -ENODEV;
1552 goto mdp_probe_err;
1553 }
1554#ifdef CONFIG_FB_MSM_MDP40
1555 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
1556 mdp4_display_intf = inpdw(MDP_BASE + 0x0038);
1557 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
1558#endif
1559
1560#ifdef CONFIG_MSM_BUS_SCALING
1561 if (!mdp_bus_scale_handle && mdp_pdata &&
1562 mdp_pdata->mdp_bus_scale_table) {
1563 mdp_bus_scale_handle =
1564 msm_bus_scale_register_client(
1565 mdp_pdata->mdp_bus_scale_table);
1566 if (!mdp_bus_scale_handle) {
1567 printk(KERN_ERR "%s not able to get bus scale\n",
1568 __func__);
1569 return -ENOMEM;
1570 }
1571 }
1572#endif
1573 /* set driver data */
1574 platform_set_drvdata(msm_fb_dev, mfd);
1575
1576 rc = platform_device_add(msm_fb_dev);
1577 if (rc) {
1578 goto mdp_probe_err;
1579 }
1580
1581 pm_runtime_set_active(&pdev->dev);
1582 pm_runtime_enable(&pdev->dev);
1583
1584 pdev_list[pdev_list_cnt++] = pdev;
1585 mdp4_extn_disp = 0;
1586 return 0;
1587
1588 mdp_probe_err:
1589 platform_device_put(msm_fb_dev);
1590#ifdef CONFIG_MSM_BUS_SCALING
1591 if (mdp_pdata && mdp_pdata->mdp_bus_scale_table &&
1592 mdp_bus_scale_handle > 0)
1593 msm_bus_scale_unregister_client(mdp_bus_scale_handle);
1594#endif
1595 return rc;
1596}
1597
1598#ifdef CONFIG_PM
1599static void mdp_suspend_sub(void)
1600{
1601 /* cancel pipe ctrl worker */
1602 cancel_delayed_work(&mdp_pipe_ctrl_worker);
1603
1604 /* for workder can't be cancelled... */
1605 flush_workqueue(mdp_pipe_ctrl_wq);
1606
1607 /* let's wait for PPP completion */
1608 while (atomic_read(&mdp_block_power_cnt[MDP_PPP_BLOCK]) > 0)
1609 cpu_relax();
1610
1611 /* try to power down */
1612 mdp_pipe_ctrl(MDP_MASTER_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
1613
1614 mutex_lock(&mdp_suspend_mutex);
1615 mdp_suspended = TRUE;
1616 mutex_unlock(&mdp_suspend_mutex);
1617}
1618#endif
1619
1620#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND)
1621static int mdp_suspend(struct platform_device *pdev, pm_message_t state)
1622{
1623 if (pdev->id == 0) {
1624 mdp_suspend_sub();
1625 if (mdp_current_clk_on) {
1626 printk(KERN_WARNING"MDP suspend failed\n");
1627 return -EBUSY;
1628 }
1629 }
1630
1631 return 0;
1632}
1633#endif
1634
1635#ifdef CONFIG_HAS_EARLYSUSPEND
1636static void mdp_early_suspend(struct early_suspend *h)
1637{
1638 mdp_suspend_sub();
Ravishangar Kalyanamdf021cf2011-10-20 12:53:27 -07001639#ifdef CONFIG_FB_MSM_DTV
1640 mdp4_dtv_set_black_screen();
1641#endif
Ravishangar Kalyanama7c98912011-09-22 12:44:15 -07001642 if (footswitch && mdp_rev > MDP_REV_42)
Ravishangar Kalyanam419051b2011-08-31 19:07:53 -07001643 regulator_disable(footswitch);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001644}
1645
1646static void mdp_early_resume(struct early_suspend *h)
1647{
Ravishangar Kalyanama7c98912011-09-22 12:44:15 -07001648 if (footswitch && mdp_rev > MDP_REV_42)
Ravishangar Kalyanam419051b2011-08-31 19:07:53 -07001649 regulator_enable(footswitch);
1650
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001651 mutex_lock(&mdp_suspend_mutex);
1652 mdp_suspended = FALSE;
1653 mutex_unlock(&mdp_suspend_mutex);
1654}
1655#endif
1656
1657static int mdp_remove(struct platform_device *pdev)
1658{
1659 if (footswitch != NULL)
1660 regulator_put(footswitch);
1661 iounmap(msm_mdp_base);
1662 pm_runtime_disable(&pdev->dev);
1663#ifdef CONFIG_MSM_BUS_SCALING
1664 if (mdp_pdata && mdp_pdata->mdp_bus_scale_table &&
1665 mdp_bus_scale_handle > 0)
1666 msm_bus_scale_unregister_client(mdp_bus_scale_handle);
1667#endif
1668 return 0;
1669}
1670
1671static int mdp_register_driver(void)
1672{
1673#ifdef CONFIG_HAS_EARLYSUSPEND
1674 early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 1;
1675 early_suspend.suspend = mdp_early_suspend;
1676 early_suspend.resume = mdp_early_resume;
1677 register_early_suspend(&early_suspend);
1678#endif
1679
1680 return platform_driver_register(&mdp_driver);
1681}
1682
1683static int __init mdp_driver_init(void)
1684{
1685 int ret;
1686
1687 mdp_drv_init();
1688
1689 ret = mdp_register_driver();
1690 if (ret) {
1691 printk(KERN_ERR "mdp_register_driver() failed!\n");
1692 return ret;
1693 }
1694
1695#if defined(CONFIG_DEBUG_FS)
1696 mdp_debugfs_init();
1697#endif
1698
1699 return 0;
1700
1701}
1702
1703module_init(mdp_driver_init);