blob: 0b6f7f702dc6cc7f870c1a0bb70398d2043d06cd [file] [log] [blame]
Pavel Machekd480ace2009-09-22 16:47:03 -07001/* drivers/video/msm/msm_fb.c
2 *
3 * Core MSM framebuffer driver.
4 *
5 * Copyright (C) 2007 Google Incorporated
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07006 * Copyright (c) 2008-2011, Code Aurora Forum. All rights reserved.
Pavel Machekd480ace2009-09-22 16:47:03 -07007 *
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
Pavel Machekd480ace2009-09-22 16:47:03 -070018#include <linux/module.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070019#include <linux/moduleparam.h>
20#include <linux/kernel.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090021#include <linux/slab.h>
Pavel Machekd480ace2009-09-22 16:47:03 -070022#include <linux/delay.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070023#include <linux/mm.h>
24#include <linux/fb.h>
Pavel Machekd480ace2009-09-22 16:47:03 -070025#include <linux/msm_mdp.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070026#include <linux/init.h>
27#include <linux/ioport.h>
28#include <linux/device.h>
Pavel Machekd480ace2009-09-22 16:47:03 -070029#include <linux/dma-mapping.h>
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070030#include <mach/board.h>
31#include <linux/uaccess.h>
Pavel Machekd480ace2009-09-22 16:47:03 -070032
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070033#include <linux/workqueue.h>
34#include <linux/string.h>
35#include <linux/version.h>
36#include <linux/proc_fs.h>
37#include <linux/vmalloc.h>
38#include <linux/debugfs.h>
39#include <linux/console.h>
40#include <linux/android_pmem.h>
41#include <linux/leds.h>
42#include <linux/pm_runtime.h>
Pavel Machekd480ace2009-09-22 16:47:03 -070043
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070044#define MSM_FB_C
45#include "msm_fb.h"
46#include "mddihosti.h"
47#include "tvenc.h"
48#include "mdp.h"
49#include "mdp4.h"
Pavel Machekd480ace2009-09-22 16:47:03 -070050
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070051#ifdef CONFIG_FB_MSM_LOGO
52#define INIT_IMAGE_FILE "/initlogo.rle"
53extern int load_565rle_image(char *filename);
54#endif
Pavel Machekd480ace2009-09-22 16:47:03 -070055
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070056#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER
57#define MSM_FB_NUM 3
58#endif
Pavel Machekd480ace2009-09-22 16:47:03 -070059
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070060static unsigned char *fbram;
61static unsigned char *fbram_phys;
62static int fbram_size;
Pavel Machekd480ace2009-09-22 16:47:03 -070063
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070064static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST];
65static int pdev_list_cnt;
Pavel Machekd480ace2009-09-22 16:47:03 -070066
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070067int vsync_mode = 1;
Pavel Machekd480ace2009-09-22 16:47:03 -070068
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070069#define MAX_BLIT_REQ 256
70
71#define MAX_FBI_LIST 32
72static struct fb_info *fbi_list[MAX_FBI_LIST];
73static int fbi_list_index;
74
75static struct msm_fb_data_type *mfd_list[MAX_FBI_LIST];
76static int mfd_list_index;
77
78static u32 msm_fb_pseudo_palette[16] = {
79 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff,
80 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
81 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
82 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff
Pavel Machekd480ace2009-09-22 16:47:03 -070083};
84
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -070085u32 msm_fb_debug_enabled;
86/* Setting msm_fb_msg_level to 8 prints out ALL messages */
87u32 msm_fb_msg_level = 7;
88
89/* Setting mddi_msg_level to 8 prints out ALL messages */
90u32 mddi_msg_level = 5;
91
92extern int32 mdp_block_power_cnt[MDP_MAX_BLOCK];
93extern unsigned long mdp_timer_duration;
94
95static int msm_fb_register(struct msm_fb_data_type *mfd);
96static int msm_fb_open(struct fb_info *info, int user);
97static int msm_fb_release(struct fb_info *info, int user);
98static int msm_fb_pan_display(struct fb_var_screeninfo *var,
99 struct fb_info *info);
100static int msm_fb_stop_sw_refresher(struct msm_fb_data_type *mfd);
101int msm_fb_resume_sw_refresher(struct msm_fb_data_type *mfd);
102static int msm_fb_check_var(struct fb_var_screeninfo *var,
103 struct fb_info *info);
104static int msm_fb_set_par(struct fb_info *info);
105static int msm_fb_blank_sub(int blank_mode, struct fb_info *info,
106 boolean op_enable);
107static int msm_fb_suspend_sub(struct msm_fb_data_type *mfd);
108static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd,
109 unsigned long arg);
110static int msm_fb_mmap(struct fb_info *info, struct vm_area_struct * vma);
111
112#ifdef MSM_FB_ENABLE_DBGFS
113
114#define MSM_FB_MAX_DBGFS 1024
115#define MAX_BACKLIGHT_BRIGHTNESS 255
116
117int msm_fb_debugfs_file_index;
118struct dentry *msm_fb_debugfs_root;
119struct dentry *msm_fb_debugfs_file[MSM_FB_MAX_DBGFS];
120
121DEFINE_MUTEX(msm_fb_notify_update_sem);
122void msmfb_no_update_notify_timer_cb(unsigned long data)
Pavel Machekd480ace2009-09-22 16:47:03 -0700123{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700124 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)data;
125 if (!mfd)
126 pr_err("%s mfd NULL\n", __func__);
127 complete(&mfd->msmfb_no_update_notify);
Pavel Machekd480ace2009-09-22 16:47:03 -0700128}
129
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700130struct dentry *msm_fb_get_debugfs_root(void)
Pavel Machekd480ace2009-09-22 16:47:03 -0700131{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700132 if (msm_fb_debugfs_root == NULL)
133 msm_fb_debugfs_root = debugfs_create_dir("msm_fb", NULL);
134
135 return msm_fb_debugfs_root;
Pavel Machekd480ace2009-09-22 16:47:03 -0700136}
137
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700138void msm_fb_debugfs_file_create(struct dentry *root, const char *name,
139 u32 *var)
Pavel Machekd480ace2009-09-22 16:47:03 -0700140{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700141 if (msm_fb_debugfs_file_index >= MSM_FB_MAX_DBGFS)
Pavel Machekd480ace2009-09-22 16:47:03 -0700142 return;
Pavel Machekd480ace2009-09-22 16:47:03 -0700143
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700144 msm_fb_debugfs_file[msm_fb_debugfs_file_index++] =
145 debugfs_create_u32(name, S_IRUGO | S_IWUSR, root, var);
Pavel Machekd480ace2009-09-22 16:47:03 -0700146}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700147#endif
Pavel Machekd480ace2009-09-22 16:47:03 -0700148
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700149int msm_fb_cursor(struct fb_info *info, struct fb_cursor *cursor)
Pavel Machekd480ace2009-09-22 16:47:03 -0700150{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700151 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
152
153 if (!mfd->cursor_update)
154 return -ENODEV;
155
156 return mfd->cursor_update(info, cursor);
Pavel Machekd480ace2009-09-22 16:47:03 -0700157}
158
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700159static int msm_fb_resource_initialized;
160
161#ifndef CONFIG_FB_BACKLIGHT
162static int lcd_backlight_registered;
163
164static void msm_fb_set_bl_brightness(struct led_classdev *led_cdev,
165 enum led_brightness value)
Pavel Machekd480ace2009-09-22 16:47:03 -0700166{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700167 struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent);
168 int bl_lvl;
Pavel Machekd480ace2009-09-22 16:47:03 -0700169
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700170 if (value > MAX_BACKLIGHT_BRIGHTNESS)
171 value = MAX_BACKLIGHT_BRIGHTNESS;
172
173 /* This maps android backlight level 0 to 255 into
174 driver backlight level 0 to bl_max with rounding */
175 bl_lvl = (2 * value * mfd->panel_info.bl_max + MAX_BACKLIGHT_BRIGHTNESS)
176 /(2 * MAX_BACKLIGHT_BRIGHTNESS);
177
178 if (!bl_lvl && value)
179 bl_lvl = 1;
180
181 msm_fb_set_backlight(mfd, bl_lvl);
Pavel Machekd480ace2009-09-22 16:47:03 -0700182}
183
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700184static struct led_classdev backlight_led = {
185 .name = "lcd-backlight",
186 .brightness = MAX_BACKLIGHT_BRIGHTNESS,
187 .brightness_set = msm_fb_set_bl_brightness,
Pavel Machekd480ace2009-09-22 16:47:03 -0700188};
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700189#endif
Pavel Machekd480ace2009-09-22 16:47:03 -0700190
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700191static struct msm_fb_platform_data *msm_fb_pdata;
192static char panel_name[128];
193module_param_string(panel_name, panel_name, sizeof(panel_name) , 0);
Pavel Machekd480ace2009-09-22 16:47:03 -0700194
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700195int msm_fb_detect_client(const char *name)
Pavel Machekd480ace2009-09-22 16:47:03 -0700196{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700197 int ret = -EPERM;
198#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT
199 u32 id;
200#endif
Pavel Machekd480ace2009-09-22 16:47:03 -0700201
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700202 MSM_FB_DEBUG("\n name = %s, panel_name = %s", name, panel_name);
203 if (strlen(panel_name)) {
204 if (!strcmp((char *)panel_name, name))
205 return 0;
206 else
207 return -EPERM;
Pavel Machekd480ace2009-09-22 16:47:03 -0700208 }
209
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700210 if (msm_fb_pdata && msm_fb_pdata->detect_client) {
211 ret = msm_fb_pdata->detect_client(name);
Pavel Machekd480ace2009-09-22 16:47:03 -0700212
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700213 /* if it's non mddi panel, we need to pre-scan
214 mddi client to see if we can disable mddi host */
Pavel Machekd480ace2009-09-22 16:47:03 -0700215
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700216#ifdef CONFIG_FB_MSM_MDDI_AUTO_DETECT
217 if (!ret && msm_fb_pdata->mddi_prescan)
218 id = mddi_get_client_id();
219#endif
Pavel Machekd480ace2009-09-22 16:47:03 -0700220 }
221
Pavel Machekd480ace2009-09-22 16:47:03 -0700222 return ret;
223}
224
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700225static ssize_t msm_fb_msm_fb_type(struct device *dev,
226 struct device_attribute *attr, char *buf)
227{
228 ssize_t ret = 0;
229 struct fb_info *fbi = dev_get_drvdata(dev);
230 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
231 struct msm_fb_panel_data *pdata =
232 (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
233
234 switch (pdata->panel_info.type) {
235 case NO_PANEL:
236 ret = snprintf(buf, PAGE_SIZE, "no panel\n");
237 break;
238 case MDDI_PANEL:
239 ret = snprintf(buf, PAGE_SIZE, "mddi panel\n");
240 break;
241 case EBI2_PANEL:
242 ret = snprintf(buf, PAGE_SIZE, "ebi2 panel\n");
243 break;
244 case LCDC_PANEL:
245 ret = snprintf(buf, PAGE_SIZE, "lcdc panel\n");
246 break;
247 case EXT_MDDI_PANEL:
248 ret = snprintf(buf, PAGE_SIZE, "ext mddi panel\n");
249 break;
250 case TV_PANEL:
251 ret = snprintf(buf, PAGE_SIZE, "tv panel\n");
252 break;
253 case HDMI_PANEL:
254 ret = snprintf(buf, PAGE_SIZE, "hdmi panel\n");
255 break;
256 case DTV_PANEL:
257 ret = snprintf(buf, PAGE_SIZE, "dtv panel\n");
258 break;
259 default:
260 ret = snprintf(buf, PAGE_SIZE, "unknown panel\n");
261 break;
262 }
263
264 return ret;
265}
266
267static DEVICE_ATTR(msm_fb_type, S_IRUGO, msm_fb_msm_fb_type, NULL);
268static struct attribute *msm_fb_attrs[] = {
269 &dev_attr_msm_fb_type.attr,
270 NULL,
271};
272static struct attribute_group msm_fb_attr_group = {
273 .attrs = msm_fb_attrs,
Pavel Machekd480ace2009-09-22 16:47:03 -0700274};
275
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700276static int msm_fb_create_sysfs(struct platform_device *pdev)
Pavel Machekd480ace2009-09-22 16:47:03 -0700277{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700278 int rc;
279 struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
280
281 rc = sysfs_create_group(&mfd->fbi->dev->kobj, &msm_fb_attr_group);
282 if (rc)
283 MSM_FB_ERR("%s: sysfs group creation failed, rc=%d\n", __func__,
284 rc);
285 return rc;
286}
287static void msm_fb_remove_sysfs(struct platform_device *pdev)
288{
289 struct msm_fb_data_type *mfd = platform_get_drvdata(pdev);
290 sysfs_remove_group(&mfd->fbi->dev->kobj, &msm_fb_attr_group);
291}
292
293static int msm_fb_probe(struct platform_device *pdev)
294{
295 struct msm_fb_data_type *mfd;
296 int rc;
297 int err = 0;
298
299 MSM_FB_DEBUG("msm_fb_probe\n");
300
301 if ((pdev->id == 0) && (pdev->num_resources > 0)) {
302 msm_fb_pdata = pdev->dev.platform_data;
303 fbram_size =
304 pdev->resource[0].end - pdev->resource[0].start + 1;
305 fbram_phys = (char *)pdev->resource[0].start;
Ravishangar Kalyanam7b57bb82011-07-19 19:14:23 -0700306 fbram = __va(fbram_phys);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700307
308 if (!fbram) {
309 printk(KERN_ERR "fbram ioremap failed!\n");
310 return -ENOMEM;
311 }
312 MSM_FB_DEBUG("msm_fb_probe: phy_Addr = 0x%x virt = 0x%x\n",
313 (int)fbram_phys, (int)fbram);
314
315 msm_fb_resource_initialized = 1;
Pavel Machekd480ace2009-09-22 16:47:03 -0700316 return 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700317 }
318
319 if (!msm_fb_resource_initialized)
320 return -EPERM;
321
322 mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
323
324 if (!mfd)
325 return -ENODEV;
326
327 if (mfd->key != MFD_KEY)
328 return -EINVAL;
329
330 if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST)
331 return -ENOMEM;
332
333 mfd->panel_info.frame_count = 0;
334 mfd->bl_level = 0;
335#ifdef CONFIG_FB_MSM_OVERLAY
336 mfd->overlay_play_enable = 1;
337#endif
338 rc = msm_fb_register(mfd);
339 if (rc)
340 return rc;
341 err = pm_runtime_set_active(mfd->fbi->dev);
342 if (err < 0)
343 printk(KERN_ERR "pm_runtime: fail to set active.\n");
344 pm_runtime_enable(mfd->fbi->dev);
345#ifdef CONFIG_FB_BACKLIGHT
346 msm_fb_config_backlight(mfd);
347#else
348 /* android supports only one lcd-backlight/lcd for now */
349 if (!lcd_backlight_registered) {
350 if (led_classdev_register(&pdev->dev, &backlight_led))
351 printk(KERN_ERR "led_classdev_register failed\n");
352 else
353 lcd_backlight_registered = 1;
354 }
355#endif
356
357 pdev_list[pdev_list_cnt++] = pdev;
358 msm_fb_create_sysfs(pdev);
359 return 0;
Pavel Machekd480ace2009-09-22 16:47:03 -0700360}
361
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700362static int msm_fb_remove(struct platform_device *pdev)
Pavel Machekd480ace2009-09-22 16:47:03 -0700363{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700364 struct msm_fb_data_type *mfd;
365
366 MSM_FB_DEBUG("msm_fb_remove\n");
367
368 mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
369
370 msm_fb_remove_sysfs(pdev);
371
372 pm_runtime_disable(mfd->fbi->dev);
373
374 if (!mfd)
375 return -ENODEV;
376
377 if (mfd->key != MFD_KEY)
378 return -EINVAL;
379
380 if (msm_fb_suspend_sub(mfd))
381 printk(KERN_ERR "msm_fb_remove: can't stop the device %d\n", mfd->index);
382
383 if (mfd->channel_irq != 0)
384 free_irq(mfd->channel_irq, (void *)mfd);
385
386 if (mfd->vsync_width_boundary)
387 vfree(mfd->vsync_width_boundary);
388
389 if (mfd->vsync_resync_timer.function)
390 del_timer(&mfd->vsync_resync_timer);
391
392 if (mfd->refresh_timer.function)
393 del_timer(&mfd->refresh_timer);
394
395 if (mfd->dma_hrtimer.function)
396 hrtimer_cancel(&mfd->dma_hrtimer);
397
398 if (mfd->msmfb_no_update_notify_timer.function)
399 del_timer(&mfd->msmfb_no_update_notify_timer);
400 complete(&mfd->msmfb_no_update_notify);
401 complete(&mfd->msmfb_update_notify);
402
403 /* remove /dev/fb* */
404 unregister_framebuffer(mfd->fbi);
405
406#ifdef CONFIG_FB_BACKLIGHT
407 /* remove /sys/class/backlight */
408 backlight_device_unregister(mfd->fbi->bl_dev);
409#else
410 if (lcd_backlight_registered) {
411 lcd_backlight_registered = 0;
412 led_classdev_unregister(&backlight_led);
413 }
414#endif
415
416#ifdef MSM_FB_ENABLE_DBGFS
417 if (mfd->sub_dir)
418 debugfs_remove(mfd->sub_dir);
419#endif
420
421 return 0;
Pavel Machekd480ace2009-09-22 16:47:03 -0700422}
423
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700424#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND)
425static int msm_fb_suspend(struct platform_device *pdev, pm_message_t state)
426{
427 struct msm_fb_data_type *mfd;
428 int ret = 0;
429
430 MSM_FB_DEBUG("msm_fb_suspend\n");
431
432 mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
433
434 if ((!mfd) || (mfd->key != MFD_KEY))
435 return 0;
436
437 console_lock();
438 fb_set_suspend(mfd->fbi, FBINFO_STATE_SUSPENDED);
439
440 ret = msm_fb_suspend_sub(mfd);
441 if (ret != 0) {
442 printk(KERN_ERR "msm_fb: failed to suspend! %d\n", ret);
443 fb_set_suspend(mfd->fbi, FBINFO_STATE_RUNNING);
444 } else {
445 pdev->dev.power.power_state = state;
446 }
447
448 console_unlock();
449 return ret;
450}
451#else
452#define msm_fb_suspend NULL
453#endif
454
455static int msm_fb_suspend_sub(struct msm_fb_data_type *mfd)
456{
457 int ret = 0;
458
459 if ((!mfd) || (mfd->key != MFD_KEY))
460 return 0;
461
462 if (mfd->msmfb_no_update_notify_timer.function)
463 del_timer(&mfd->msmfb_no_update_notify_timer);
464 complete(&mfd->msmfb_no_update_notify);
465
466 /*
467 * suspend this channel
468 */
469 mfd->suspend.sw_refreshing_enable = mfd->sw_refreshing_enable;
470 mfd->suspend.op_enable = mfd->op_enable;
471 mfd->suspend.panel_power_on = mfd->panel_power_on;
472
473 if (mfd->op_enable) {
474 ret =
475 msm_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi,
476 mfd->suspend.op_enable);
477 if (ret) {
478 MSM_FB_INFO
479 ("msm_fb_suspend: can't turn off display!\n");
480 return ret;
481 }
482 mfd->op_enable = FALSE;
483 }
484 /*
485 * try to power down
486 */
487 mdp_pipe_ctrl(MDP_MASTER_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
488
489 /*
490 * detach display channel irq if there's any
491 * or wait until vsync-resync completes
492 */
493 if ((mfd->dest == DISPLAY_LCD)) {
494 if (mfd->panel_info.lcd.vsync_enable) {
495 if (mfd->panel_info.lcd.hw_vsync_mode) {
496 if (mfd->channel_irq != 0)
497 disable_irq(mfd->channel_irq);
498 } else {
499 volatile boolean vh_pending;
500 do {
501 vh_pending = mfd->vsync_handler_pending;
502 } while (vh_pending);
503 }
504 }
505 }
506
507 return 0;
508}
509
510#ifdef CONFIG_PM
511static int msm_fb_resume_sub(struct msm_fb_data_type *mfd)
512{
513 int ret = 0;
514
515 if ((!mfd) || (mfd->key != MFD_KEY))
516 return 0;
517
518 /* attach display channel irq if there's any */
519 if (mfd->channel_irq != 0)
520 enable_irq(mfd->channel_irq);
521
522 /* resume state var recover */
523 mfd->sw_refreshing_enable = mfd->suspend.sw_refreshing_enable;
524 mfd->op_enable = mfd->suspend.op_enable;
525
526 if (mfd->suspend.panel_power_on) {
527 ret =
528 msm_fb_blank_sub(FB_BLANK_UNBLANK, mfd->fbi,
529 mfd->op_enable);
530 if (ret)
531 MSM_FB_INFO("msm_fb_resume: can't turn on display!\n");
532 }
533
534 return ret;
535}
536#endif
537
538#if defined(CONFIG_PM) && !defined(CONFIG_HAS_EARLYSUSPEND)
539static int msm_fb_resume(struct platform_device *pdev)
540{
541 /* This resume function is called when interrupt is enabled.
542 */
543 int ret = 0;
544 struct msm_fb_data_type *mfd;
545
546 MSM_FB_DEBUG("msm_fb_resume\n");
547
548 mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev);
549
550 if ((!mfd) || (mfd->key != MFD_KEY))
551 return 0;
552
553 console_lock();
554 ret = msm_fb_resume_sub(mfd);
555 pdev->dev.power.power_state = PMSG_ON;
556 fb_set_suspend(mfd->fbi, FBINFO_STATE_RUNNING);
557 console_unlock();
558
559 return ret;
560}
561#else
562#define msm_fb_resume NULL
563#endif
564
565static int msm_fb_runtime_suspend(struct device *dev)
566{
567 dev_dbg(dev, "pm_runtime: suspending...\n");
568 return 0;
569}
570
571static int msm_fb_runtime_resume(struct device *dev)
572{
573 dev_dbg(dev, "pm_runtime: resuming...\n");
574 return 0;
575}
576
577static int msm_fb_runtime_idle(struct device *dev)
578{
579 dev_dbg(dev, "pm_runtime: idling...\n");
580 return 0;
581}
582
583static struct dev_pm_ops msm_fb_dev_pm_ops = {
584 .runtime_suspend = msm_fb_runtime_suspend,
585 .runtime_resume = msm_fb_runtime_resume,
586 .runtime_idle = msm_fb_runtime_idle,
Pavel Machekd480ace2009-09-22 16:47:03 -0700587};
588
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700589static struct platform_driver msm_fb_driver = {
590 .probe = msm_fb_probe,
591 .remove = msm_fb_remove,
592#ifndef CONFIG_HAS_EARLYSUSPEND
593 .suspend = msm_fb_suspend,
594 .resume = msm_fb_resume,
595#endif
596 .shutdown = NULL,
597 .driver = {
598 /* Driver name must match the device name added in platform.c. */
599 .name = "msm_fb",
600 .pm = &msm_fb_dev_pm_ops,
601 },
602};
603
604#if defined(CONFIG_HAS_EARLYSUSPEND) && (defined(CONFIG_FB_MSM_OVERLAY) || \
605 defined(CONFIG_FB_MSM_MDP303))
606static void memset32_io(u32 __iomem *_ptr, u32 val, size_t count)
Pavel Machekd480ace2009-09-22 16:47:03 -0700607{
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700608 count >>= 2;
609 while (count--)
610 writel(val, _ptr++);
611}
612#endif
613
614#ifdef CONFIG_HAS_EARLYSUSPEND
615static void msmfb_early_suspend(struct early_suspend *h)
616{
617 struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
618 early_suspend);
619#if defined(CONFIG_FB_MSM_OVERLAY) || defined(CONFIG_FB_MSM_MDP303)
620 /*
621 * For MDP with overlay, set framebuffer with black pixels
622 * to show black screen on HDMI.
623 */
624 struct fb_info *fbi = mfd->fbi;
625 switch (mfd->fbi->var.bits_per_pixel) {
626 case 32:
627 memset32_io((void *)fbi->screen_base, 0xFF000000,
628 fbi->fix.smem_len);
629 break;
630 default:
631 memset32_io((void *)fbi->screen_base, 0x00, fbi->fix.smem_len);
632 break;
633 }
634#endif
635 msm_fb_suspend_sub(mfd);
Pavel Machekd480ace2009-09-22 16:47:03 -0700636}
637
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700638static void msmfb_early_resume(struct early_suspend *h)
639{
640 struct msm_fb_data_type *mfd = container_of(h, struct msm_fb_data_type,
641 early_suspend);
642 msm_fb_resume_sub(mfd);
643}
644#endif
645
646void msm_fb_set_backlight(struct msm_fb_data_type *mfd, __u32 bkl_lvl)
647{
648 struct msm_fb_panel_data *pdata;
649
650 pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
651
652 if ((pdata) && (pdata->set_backlight)) {
653 down(&mfd->sem);
654 mfd->bl_level = bkl_lvl;
655 pdata->set_backlight(mfd);
656 up(&mfd->sem);
657 }
658}
659
660static int msm_fb_blank_sub(int blank_mode, struct fb_info *info,
661 boolean op_enable)
662{
663 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
664 struct msm_fb_panel_data *pdata = NULL;
665 int ret = 0;
666
667 if (!op_enable)
668 return -EPERM;
669
670 pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
671 if ((!pdata) || (!pdata->on) || (!pdata->off)) {
672 printk(KERN_ERR "msm_fb_blank_sub: no panel operation detected!\n");
673 return -ENODEV;
674 }
675
676 switch (blank_mode) {
677 case FB_BLANK_UNBLANK:
678 if (!mfd->panel_power_on) {
679 msleep(16);
680 ret = pdata->on(mfd->pdev);
681 if (ret == 0) {
682 mfd->panel_power_on = TRUE;
683
684/* ToDo: possible conflict with android which doesn't expect sw refresher */
685/*
686 if (!mfd->hw_refresh)
687 {
688 if ((ret = msm_fb_resume_sw_refresher(mfd)) != 0)
689 {
690 MSM_FB_INFO("msm_fb_blank_sub: msm_fb_resume_sw_refresher failed = %d!\n",ret);
691 }
692 }
693*/
694 }
695 }
696 break;
697
698 case FB_BLANK_VSYNC_SUSPEND:
699 case FB_BLANK_HSYNC_SUSPEND:
700 case FB_BLANK_NORMAL:
701 case FB_BLANK_POWERDOWN:
702 default:
703 if (mfd->panel_power_on) {
704 int curr_pwr_state;
705
706 mfd->op_enable = FALSE;
707 curr_pwr_state = mfd->panel_power_on;
708 mfd->panel_power_on = FALSE;
709
710 msleep(16);
711 ret = pdata->off(mfd->pdev);
712 if (ret)
713 mfd->panel_power_on = curr_pwr_state;
714
715 mfd->op_enable = TRUE;
716 }
717 break;
718 }
719
720 return ret;
721}
722
723static void msm_fb_fillrect(struct fb_info *info,
724 const struct fb_fillrect *rect)
725{
726 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
727
728 cfb_fillrect(info, rect);
729 if (!mfd->hw_refresh && (info->var.yoffset == 0) &&
730 !mfd->sw_currently_refreshing) {
731 struct fb_var_screeninfo var;
732
733 var = info->var;
734 var.reserved[0] = 0x54445055;
735 var.reserved[1] = (rect->dy << 16) | (rect->dx);
736 var.reserved[2] = ((rect->dy + rect->height) << 16) |
737 (rect->dx + rect->width);
738
739 msm_fb_pan_display(&var, info);
740 }
741}
742
743static void msm_fb_copyarea(struct fb_info *info,
744 const struct fb_copyarea *area)
745{
746 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
747
748 cfb_copyarea(info, area);
749 if (!mfd->hw_refresh && (info->var.yoffset == 0) &&
750 !mfd->sw_currently_refreshing) {
751 struct fb_var_screeninfo var;
752
753 var = info->var;
754 var.reserved[0] = 0x54445055;
755 var.reserved[1] = (area->dy << 16) | (area->dx);
756 var.reserved[2] = ((area->dy + area->height) << 16) |
757 (area->dx + area->width);
758
759 msm_fb_pan_display(&var, info);
760 }
761}
762
763static void msm_fb_imageblit(struct fb_info *info, const struct fb_image *image)
764{
765 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
766
767 cfb_imageblit(info, image);
768 if (!mfd->hw_refresh && (info->var.yoffset == 0) &&
769 !mfd->sw_currently_refreshing) {
770 struct fb_var_screeninfo var;
771
772 var = info->var;
773 var.reserved[0] = 0x54445055;
774 var.reserved[1] = (image->dy << 16) | (image->dx);
775 var.reserved[2] = ((image->dy + image->height) << 16) |
776 (image->dx + image->width);
777
778 msm_fb_pan_display(&var, info);
779 }
780}
781
782static int msm_fb_blank(int blank_mode, struct fb_info *info)
783{
784 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
785 return msm_fb_blank_sub(blank_mode, info, mfd->op_enable);
786}
787
788static int msm_fb_set_lut(struct fb_cmap *cmap, struct fb_info *info)
789{
790 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
791
792 if (!mfd->lut_update)
793 return -ENODEV;
794
795 mfd->lut_update(info, cmap);
796 return 0;
797}
798
799/*
800 * Custom Framebuffer mmap() function for MSM driver.
801 * Differs from standard mmap() function by allowing for customized
802 * page-protection.
803 */
804static int msm_fb_mmap(struct fb_info *info, struct vm_area_struct * vma)
805{
806 /* Get frame buffer memory range. */
807 unsigned long start = info->fix.smem_start;
808 u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
809 unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
810 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
811 if (off >= len) {
812 /* memory mapped io */
813 off -= len;
814 if (info->var.accel_flags) {
815 mutex_unlock(&info->lock);
816 return -EINVAL;
817 }
818 start = info->fix.mmio_start;
819 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
820 }
821
822 /* Set VM flags. */
823 start &= PAGE_MASK;
824 if ((vma->vm_end - vma->vm_start + off) > len)
825 return -EINVAL;
826 off += start;
827 vma->vm_pgoff = off >> PAGE_SHIFT;
828 /* This is an IO map - tell maydump to skip this VMA */
829 vma->vm_flags |= VM_IO | VM_RESERVED;
830
831 /* Set VM page protection */
832 if (mfd->mdp_fb_page_protection == MDP_FB_PAGE_PROTECTION_WRITECOMBINE)
833 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
834 else if (mfd->mdp_fb_page_protection ==
835 MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE)
836 vma->vm_page_prot = pgprot_writethroughcache(vma->vm_page_prot);
837 else if (mfd->mdp_fb_page_protection ==
838 MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE)
839 vma->vm_page_prot = pgprot_writebackcache(vma->vm_page_prot);
840 else if (mfd->mdp_fb_page_protection ==
841 MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE)
842 vma->vm_page_prot = pgprot_writebackwacache(vma->vm_page_prot);
843 else
844 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
845
846 /* Remap the frame buffer I/O range */
847 if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
848 vma->vm_end - vma->vm_start,
849 vma->vm_page_prot))
850 return -EAGAIN;
851
852 return 0;
853}
854
855static struct fb_ops msm_fb_ops = {
856 .owner = THIS_MODULE,
857 .fb_open = msm_fb_open,
858 .fb_release = msm_fb_release,
859 .fb_read = NULL,
860 .fb_write = NULL,
861 .fb_cursor = NULL,
862 .fb_check_var = msm_fb_check_var, /* vinfo check */
863 .fb_set_par = msm_fb_set_par, /* set the video mode according to info->var */
864 .fb_setcolreg = NULL, /* set color register */
865 .fb_blank = msm_fb_blank, /* blank display */
866 .fb_pan_display = msm_fb_pan_display, /* pan display */
867 .fb_fillrect = msm_fb_fillrect, /* Draws a rectangle */
868 .fb_copyarea = msm_fb_copyarea, /* Copy data from area to another */
869 .fb_imageblit = msm_fb_imageblit, /* Draws a image to the display */
870 .fb_rotate = NULL,
871 .fb_sync = NULL, /* wait for blit idle, optional */
872 .fb_ioctl = msm_fb_ioctl, /* perform fb specific ioctl (optional) */
873 .fb_mmap = msm_fb_mmap,
874};
875
876static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp)
877{
878 /* The adreno GPU hardware requires that the pitch be aligned to
879 32 pixels for color buffers, so for the cases where the GPU
880 is writing directly to fb0, the framebuffer pitch
881 also needs to be 32 pixel aligned */
882
883 if (fb_index == 0)
884 return ALIGN(xres, 32) * bpp;
885 else
886 return xres * bpp;
887}
888
889static int msm_fb_register(struct msm_fb_data_type *mfd)
890{
891 int ret = -ENODEV;
892 int bpp;
893 struct msm_panel_info *panel_info = &mfd->panel_info;
894 struct fb_info *fbi = mfd->fbi;
895 struct fb_fix_screeninfo *fix;
896 struct fb_var_screeninfo *var;
897 int *id;
898 int fbram_offset;
899
900 /*
901 * fb info initialization
902 */
903 fix = &fbi->fix;
904 var = &fbi->var;
905
906 fix->type_aux = 0; /* if type == FB_TYPE_INTERLEAVED_PLANES */
907 fix->visual = FB_VISUAL_TRUECOLOR; /* True Color */
908 fix->ywrapstep = 0; /* No support */
909 fix->mmio_start = 0; /* No MMIO Address */
910 fix->mmio_len = 0; /* No MMIO Address */
911 fix->accel = FB_ACCEL_NONE;/* FB_ACCEL_MSM needes to be added in fb.h */
912
913 var->xoffset = 0, /* Offset from virtual to visible */
914 var->yoffset = 0, /* resolution */
915 var->grayscale = 0, /* No graylevels */
916 var->nonstd = 0, /* standard pixel format */
917 var->activate = FB_ACTIVATE_VBL, /* activate it at vsync */
918 var->height = -1, /* height of picture in mm */
919 var->width = -1, /* width of picture in mm */
920 var->accel_flags = 0, /* acceleration flags */
921 var->sync = 0, /* see FB_SYNC_* */
922 var->rotate = 0, /* angle we rotate counter clockwise */
923 mfd->op_enable = FALSE;
924
925 switch (mfd->fb_imgType) {
926 case MDP_RGB_565:
927 fix->type = FB_TYPE_PACKED_PIXELS;
928 fix->xpanstep = 1;
929 fix->ypanstep = 1;
930 var->vmode = FB_VMODE_NONINTERLACED;
931 var->blue.offset = 0;
932 var->green.offset = 5;
933 var->red.offset = 11;
934 var->blue.length = 5;
935 var->green.length = 6;
936 var->red.length = 5;
937 var->blue.msb_right = 0;
938 var->green.msb_right = 0;
939 var->red.msb_right = 0;
940 var->transp.offset = 0;
941 var->transp.length = 0;
942 bpp = 2;
943 break;
944
945 case MDP_RGB_888:
946 fix->type = FB_TYPE_PACKED_PIXELS;
947 fix->xpanstep = 1;
948 fix->ypanstep = 1;
949 var->vmode = FB_VMODE_NONINTERLACED;
950 var->blue.offset = 0;
951 var->green.offset = 8;
952 var->red.offset = 16;
953 var->blue.length = 8;
954 var->green.length = 8;
955 var->red.length = 8;
956 var->blue.msb_right = 0;
957 var->green.msb_right = 0;
958 var->red.msb_right = 0;
959 var->transp.offset = 0;
960 var->transp.length = 0;
961 bpp = 3;
962 break;
963
964 case MDP_ARGB_8888:
965 fix->type = FB_TYPE_PACKED_PIXELS;
966 fix->xpanstep = 1;
967 fix->ypanstep = 1;
968 var->vmode = FB_VMODE_NONINTERLACED;
969 var->blue.offset = 0;
970 var->green.offset = 8;
971 var->red.offset = 16;
972 var->blue.length = 8;
973 var->green.length = 8;
974 var->red.length = 8;
975 var->blue.msb_right = 0;
976 var->green.msb_right = 0;
977 var->red.msb_right = 0;
978 var->transp.offset = 24;
979 var->transp.length = 8;
980 bpp = 4;
981 break;
982
983 case MDP_RGBA_8888:
984 fix->type = FB_TYPE_PACKED_PIXELS;
985 fix->xpanstep = 1;
986 fix->ypanstep = 1;
987 var->vmode = FB_VMODE_NONINTERLACED;
988 var->blue.offset = 8;
989 var->green.offset = 16;
990 var->red.offset = 24;
991 var->blue.length = 8;
992 var->green.length = 8;
993 var->red.length = 8;
994 var->blue.msb_right = 0;
995 var->green.msb_right = 0;
996 var->red.msb_right = 0;
997 var->transp.offset = 0;
998 var->transp.length = 8;
999 bpp = 4;
1000 break;
1001
1002 case MDP_YCRYCB_H2V1:
1003 /* ToDo: need to check TV-Out YUV422i framebuffer format */
1004 /* we might need to create new type define */
1005 fix->type = FB_TYPE_INTERLEAVED_PLANES;
1006 fix->xpanstep = 2;
1007 fix->ypanstep = 1;
1008 var->vmode = FB_VMODE_NONINTERLACED;
1009
1010 /* how about R/G/B offset? */
1011 var->blue.offset = 0;
1012 var->green.offset = 5;
1013 var->red.offset = 11;
1014 var->blue.length = 5;
1015 var->green.length = 6;
1016 var->red.length = 5;
1017 var->blue.msb_right = 0;
1018 var->green.msb_right = 0;
1019 var->red.msb_right = 0;
1020 var->transp.offset = 0;
1021 var->transp.length = 0;
1022 bpp = 2;
1023 break;
1024
1025 default:
1026 MSM_FB_ERR("msm_fb_init: fb %d unkown image type!\n",
1027 mfd->index);
1028 return ret;
1029 }
1030
1031 fix->type = panel_info->is_3d_panel;
1032
1033 fix->line_length = msm_fb_line_length(mfd->index, panel_info->xres,
1034 bpp);
1035 /* calculate smem_len based on max size of two supplied modes */
1036 fix->smem_len = roundup(MAX(msm_fb_line_length(mfd->index,
1037 panel_info->xres,
1038 bpp) *
1039 panel_info->yres * mfd->fb_page,
1040 msm_fb_line_length(mfd->index,
1041 panel_info->mode2_xres,
1042 bpp) *
1043 panel_info->mode2_yres * mfd->fb_page), PAGE_SIZE);
1044
1045
1046
1047 mfd->var_xres = panel_info->xres;
1048 mfd->var_yres = panel_info->yres;
1049
1050 var->pixclock = mfd->panel_info.clk_rate;
1051 mfd->var_pixclock = var->pixclock;
1052
1053 var->xres = panel_info->xres;
1054 var->yres = panel_info->yres;
1055 var->xres_virtual = panel_info->xres;
1056 var->yres_virtual = panel_info->yres * mfd->fb_page;
1057 var->bits_per_pixel = bpp * 8; /* FrameBuffer color depth */
1058 if (mfd->dest == DISPLAY_LCD) {
1059 var->reserved[4] = panel_info->lcd.refx100 / 100;
1060 } else {
1061 var->reserved[4] = panel_info->clk_rate /
1062 ((panel_info->lcdc.h_back_porch +
1063 panel_info->lcdc.h_front_porch +
1064 panel_info->lcdc.h_pulse_width +
1065 panel_info->xres) *
1066 (panel_info->lcdc.v_back_porch +
1067 panel_info->lcdc.v_front_porch +
1068 panel_info->lcdc.v_pulse_width +
1069 panel_info->yres));
1070 }
1071 /*
1072 * id field for fb app
1073 */
1074 id = (int *)&mfd->panel;
1075
1076#if defined(CONFIG_FB_MSM_MDP22)
1077 snprintf(fix->id, sizeof(fix->id), "msmfb22_%x", (__u32) *id);
1078#elif defined(CONFIG_FB_MSM_MDP30)
1079 snprintf(fix->id, sizeof(fix->id), "msmfb30_%x", (__u32) *id);
1080#elif defined(CONFIG_FB_MSM_MDP31)
1081 snprintf(fix->id, sizeof(fix->id), "msmfb31_%x", (__u32) *id);
1082#elif defined(CONFIG_FB_MSM_MDP40)
1083 snprintf(fix->id, sizeof(fix->id), "msmfb40_%x", (__u32) *id);
1084#else
1085 error CONFIG_FB_MSM_MDP undefined !
1086#endif
1087 fbi->fbops = &msm_fb_ops;
1088 fbi->flags = FBINFO_FLAG_DEFAULT;
1089 fbi->pseudo_palette = msm_fb_pseudo_palette;
1090
1091 mfd->ref_cnt = 0;
1092 mfd->sw_currently_refreshing = FALSE;
1093 mfd->sw_refreshing_enable = TRUE;
1094 mfd->panel_power_on = FALSE;
1095
1096 mfd->pan_waiting = FALSE;
1097 init_completion(&mfd->pan_comp);
1098 init_completion(&mfd->refresher_comp);
1099 sema_init(&mfd->sem, 1);
1100
1101 init_timer(&mfd->msmfb_no_update_notify_timer);
1102 mfd->msmfb_no_update_notify_timer.function =
1103 msmfb_no_update_notify_timer_cb;
1104 mfd->msmfb_no_update_notify_timer.data = (unsigned long)mfd;
1105 init_completion(&mfd->msmfb_update_notify);
1106 init_completion(&mfd->msmfb_no_update_notify);
1107
1108 fbram_offset = PAGE_ALIGN((int)fbram)-(int)fbram;
1109 fbram += fbram_offset;
1110 fbram_phys += fbram_offset;
1111 fbram_size -= fbram_offset;
1112
1113 if (fbram_size < fix->smem_len) {
1114 printk(KERN_ERR "error: no more framebuffer memory!\n");
1115 return -ENOMEM;
1116 }
1117
1118 fbi->screen_base = fbram;
1119 fbi->fix.smem_start = (unsigned long)fbram_phys;
1120
1121 memset(fbi->screen_base, 0x0, fix->smem_len);
1122
1123 mfd->op_enable = TRUE;
1124 mfd->panel_power_on = FALSE;
1125
1126 /* cursor memory allocation */
1127 if (mfd->cursor_update) {
1128 mfd->cursor_buf = dma_alloc_coherent(NULL,
1129 MDP_CURSOR_SIZE,
1130 (dma_addr_t *) &mfd->cursor_buf_phys,
1131 GFP_KERNEL);
1132 if (!mfd->cursor_buf)
1133 mfd->cursor_update = 0;
1134 }
1135
1136 if (mfd->lut_update) {
1137 ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
1138 if (ret)
1139 printk(KERN_ERR "%s: fb_alloc_cmap() failed!\n",
1140 __func__);
1141 }
1142
1143 if (register_framebuffer(fbi) < 0) {
1144 if (mfd->lut_update)
1145 fb_dealloc_cmap(&fbi->cmap);
1146
1147 if (mfd->cursor_buf)
1148 dma_free_coherent(NULL,
1149 MDP_CURSOR_SIZE,
1150 mfd->cursor_buf,
1151 (dma_addr_t) mfd->cursor_buf_phys);
1152
1153 mfd->op_enable = FALSE;
1154 return -EPERM;
1155 }
1156
1157 fbram += fix->smem_len;
1158 fbram_phys += fix->smem_len;
1159 fbram_size -= fix->smem_len;
1160
1161 MSM_FB_INFO
1162 ("FrameBuffer[%d] %dx%d size=%d bytes is registered successfully!\n",
1163 mfd->index, fbi->var.xres, fbi->var.yres, fbi->fix.smem_len);
1164
1165#ifdef CONFIG_FB_MSM_LOGO
1166 if (!load_565rle_image(INIT_IMAGE_FILE)) ; /* Flip buffer */
1167#endif
1168 ret = 0;
1169
1170#ifdef CONFIG_HAS_EARLYSUSPEND
1171 if (mfd->panel_info.type != DTV_PANEL) {
1172 mfd->early_suspend.suspend = msmfb_early_suspend;
1173 mfd->early_suspend.resume = msmfb_early_resume;
1174 mfd->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 2;
1175 register_early_suspend(&mfd->early_suspend);
1176 }
1177#endif
1178
1179#ifdef MSM_FB_ENABLE_DBGFS
1180 {
1181 struct dentry *root;
1182 struct dentry *sub_dir;
1183 char sub_name[2];
1184
1185 root = msm_fb_get_debugfs_root();
1186 if (root != NULL) {
1187 sub_name[0] = (char)(mfd->index + 0x30);
1188 sub_name[1] = '\0';
1189 sub_dir = debugfs_create_dir(sub_name, root);
1190 } else {
1191 sub_dir = NULL;
1192 }
1193
1194 mfd->sub_dir = sub_dir;
1195
1196 if (sub_dir) {
1197 msm_fb_debugfs_file_create(sub_dir, "op_enable",
1198 (u32 *) &mfd->op_enable);
1199 msm_fb_debugfs_file_create(sub_dir, "panel_power_on",
1200 (u32 *) &mfd->
1201 panel_power_on);
1202 msm_fb_debugfs_file_create(sub_dir, "ref_cnt",
1203 (u32 *) &mfd->ref_cnt);
1204 msm_fb_debugfs_file_create(sub_dir, "fb_imgType",
1205 (u32 *) &mfd->fb_imgType);
1206 msm_fb_debugfs_file_create(sub_dir,
1207 "sw_currently_refreshing",
1208 (u32 *) &mfd->
1209 sw_currently_refreshing);
1210 msm_fb_debugfs_file_create(sub_dir,
1211 "sw_refreshing_enable",
1212 (u32 *) &mfd->
1213 sw_refreshing_enable);
1214
1215 msm_fb_debugfs_file_create(sub_dir, "xres",
1216 (u32 *) &mfd->panel_info.
1217 xres);
1218 msm_fb_debugfs_file_create(sub_dir, "yres",
1219 (u32 *) &mfd->panel_info.
1220 yres);
1221 msm_fb_debugfs_file_create(sub_dir, "bpp",
1222 (u32 *) &mfd->panel_info.
1223 bpp);
1224 msm_fb_debugfs_file_create(sub_dir, "type",
1225 (u32 *) &mfd->panel_info.
1226 type);
1227 msm_fb_debugfs_file_create(sub_dir, "wait_cycle",
1228 (u32 *) &mfd->panel_info.
1229 wait_cycle);
1230 msm_fb_debugfs_file_create(sub_dir, "pdest",
1231 (u32 *) &mfd->panel_info.
1232 pdest);
1233 msm_fb_debugfs_file_create(sub_dir, "backbuff",
1234 (u32 *) &mfd->panel_info.
1235 fb_num);
1236 msm_fb_debugfs_file_create(sub_dir, "clk_rate",
1237 (u32 *) &mfd->panel_info.
1238 clk_rate);
1239 msm_fb_debugfs_file_create(sub_dir, "frame_count",
1240 (u32 *) &mfd->panel_info.
1241 frame_count);
1242
1243
1244 switch (mfd->dest) {
1245 case DISPLAY_LCD:
1246 msm_fb_debugfs_file_create(sub_dir,
1247 "vsync_enable",
1248 (u32 *)&mfd->panel_info.lcd.vsync_enable);
1249 msm_fb_debugfs_file_create(sub_dir,
1250 "refx100",
1251 (u32 *) &mfd->panel_info.lcd. refx100);
1252 msm_fb_debugfs_file_create(sub_dir,
1253 "v_back_porch",
1254 (u32 *) &mfd->panel_info.lcd.v_back_porch);
1255 msm_fb_debugfs_file_create(sub_dir,
1256 "v_front_porch",
1257 (u32 *) &mfd->panel_info.lcd.v_front_porch);
1258 msm_fb_debugfs_file_create(sub_dir,
1259 "v_pulse_width",
1260 (u32 *) &mfd->panel_info.lcd.v_pulse_width);
1261 msm_fb_debugfs_file_create(sub_dir,
1262 "hw_vsync_mode",
1263 (u32 *) &mfd->panel_info.lcd.hw_vsync_mode);
1264 msm_fb_debugfs_file_create(sub_dir,
1265 "vsync_notifier_period", (u32 *)
1266 &mfd->panel_info.lcd.vsync_notifier_period);
1267 break;
1268
1269 case DISPLAY_LCDC:
1270 msm_fb_debugfs_file_create(sub_dir,
1271 "h_back_porch",
1272 (u32 *) &mfd->panel_info.lcdc.h_back_porch);
1273 msm_fb_debugfs_file_create(sub_dir,
1274 "h_front_porch",
1275 (u32 *) &mfd->panel_info.lcdc.h_front_porch);
1276 msm_fb_debugfs_file_create(sub_dir,
1277 "h_pulse_width",
1278 (u32 *) &mfd->panel_info.lcdc.h_pulse_width);
1279 msm_fb_debugfs_file_create(sub_dir,
1280 "v_back_porch",
1281 (u32 *) &mfd->panel_info.lcdc.v_back_porch);
1282 msm_fb_debugfs_file_create(sub_dir,
1283 "v_front_porch",
1284 (u32 *) &mfd->panel_info.lcdc.v_front_porch);
1285 msm_fb_debugfs_file_create(sub_dir,
1286 "v_pulse_width",
1287 (u32 *) &mfd->panel_info.lcdc.v_pulse_width);
1288 msm_fb_debugfs_file_create(sub_dir,
1289 "border_clr",
1290 (u32 *) &mfd->panel_info.lcdc.border_clr);
1291 msm_fb_debugfs_file_create(sub_dir,
1292 "underflow_clr",
1293 (u32 *) &mfd->panel_info.lcdc.underflow_clr);
1294 msm_fb_debugfs_file_create(sub_dir,
1295 "hsync_skew",
1296 (u32 *) &mfd->panel_info.lcdc.hsync_skew);
1297 break;
1298
1299 default:
1300 break;
1301 }
1302 }
1303 }
1304#endif /* MSM_FB_ENABLE_DBGFS */
1305
1306 return ret;
1307}
1308
1309static int msm_fb_open(struct fb_info *info, int user)
1310{
1311 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
1312 int result;
1313
1314 result = pm_runtime_get_sync(info->dev);
1315
1316 if (result < 0) {
1317 printk(KERN_ERR "pm_runtime: fail to wake up\n");
1318 }
1319
1320
1321 if (!mfd->ref_cnt) {
1322 mdp_set_dma_pan_info(info, NULL, TRUE);
1323
1324 if (msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable)) {
1325 printk(KERN_ERR "msm_fb_open: can't turn on display!\n");
1326 return -1;
1327 }
1328 }
1329
1330 mfd->ref_cnt++;
1331 return 0;
1332}
1333
1334static int msm_fb_release(struct fb_info *info, int user)
1335{
1336 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
1337 int ret = 0;
1338
1339 if (!mfd->ref_cnt) {
1340 MSM_FB_INFO("msm_fb_release: try to close unopened fb %d!\n",
1341 mfd->index);
1342 return -EINVAL;
1343 }
1344
1345 mfd->ref_cnt--;
1346
1347 if (!mfd->ref_cnt) {
1348 if ((ret =
1349 msm_fb_blank_sub(FB_BLANK_POWERDOWN, info,
1350 mfd->op_enable)) != 0) {
1351 printk(KERN_ERR "msm_fb_release: can't turn off display!\n");
1352 return ret;
1353 }
1354 }
1355
1356 pm_runtime_put(info->dev);
1357 return ret;
1358}
1359
1360DEFINE_SEMAPHORE(msm_fb_pan_sem);
1361
1362static int msm_fb_pan_display(struct fb_var_screeninfo *var,
1363 struct fb_info *info)
1364{
1365 struct mdp_dirty_region dirty;
1366 struct mdp_dirty_region *dirtyPtr = NULL;
1367 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
1368
1369 if ((!mfd->op_enable) || (!mfd->panel_power_on))
1370 return -EPERM;
1371
1372 if (var->xoffset > (info->var.xres_virtual - info->var.xres))
1373 return -EINVAL;
1374
1375 if (var->yoffset > (info->var.yres_virtual - info->var.yres))
1376 return -EINVAL;
1377
1378 if (info->fix.xpanstep)
1379 info->var.xoffset =
1380 (var->xoffset / info->fix.xpanstep) * info->fix.xpanstep;
1381
1382 if (info->fix.ypanstep)
1383 info->var.yoffset =
1384 (var->yoffset / info->fix.ypanstep) * info->fix.ypanstep;
1385
1386 /* "UPDT" */
1387 if (var->reserved[0] == 0x54445055) {
1388 dirty.xoffset = var->reserved[1] & 0xffff;
1389 dirty.yoffset = (var->reserved[1] >> 16) & 0xffff;
1390
1391 if ((var->reserved[2] & 0xffff) <= dirty.xoffset)
1392 return -EINVAL;
1393 if (((var->reserved[2] >> 16) & 0xffff) <= dirty.yoffset)
1394 return -EINVAL;
1395
1396 dirty.width = (var->reserved[2] & 0xffff) - dirty.xoffset;
1397 dirty.height =
1398 ((var->reserved[2] >> 16) & 0xffff) - dirty.yoffset;
1399 info->var.yoffset = var->yoffset;
1400
1401 if (dirty.xoffset < 0)
1402 return -EINVAL;
1403
1404 if (dirty.yoffset < 0)
1405 return -EINVAL;
1406
1407 if ((dirty.xoffset + dirty.width) > info->var.xres)
1408 return -EINVAL;
1409
1410 if ((dirty.yoffset + dirty.height) > info->var.yres)
1411 return -EINVAL;
1412
1413 if ((dirty.width <= 0) || (dirty.height <= 0))
1414 return -EINVAL;
1415
1416 dirtyPtr = &dirty;
1417 }
1418 complete(&mfd->msmfb_update_notify);
1419 mutex_lock(&msm_fb_notify_update_sem);
1420 if (mfd->msmfb_no_update_notify_timer.function)
1421 del_timer(&mfd->msmfb_no_update_notify_timer);
1422
1423 mfd->msmfb_no_update_notify_timer.expires =
1424 jiffies + ((1000 * HZ) / 1000);
1425 add_timer(&mfd->msmfb_no_update_notify_timer);
1426 mutex_unlock(&msm_fb_notify_update_sem);
1427
1428 down(&msm_fb_pan_sem);
1429 mdp_set_dma_pan_info(info, dirtyPtr,
1430 (var->activate == FB_ACTIVATE_VBL));
1431 mdp_dma_pan_update(info);
1432 up(&msm_fb_pan_sem);
1433
1434 ++mfd->panel_info.frame_count;
1435 return 0;
1436}
1437
1438static int msm_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
1439{
1440 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
1441
1442 if (var->rotate != FB_ROTATE_UR)
1443 return -EINVAL;
1444 if (var->grayscale != info->var.grayscale)
1445 return -EINVAL;
1446
1447 switch (var->bits_per_pixel) {
1448 case 16:
1449 if ((var->green.offset != 5) ||
1450 !((var->blue.offset == 11)
1451 || (var->blue.offset == 0)) ||
1452 !((var->red.offset == 11)
1453 || (var->red.offset == 0)) ||
1454 (var->blue.length != 5) ||
1455 (var->green.length != 6) ||
1456 (var->red.length != 5) ||
1457 (var->blue.msb_right != 0) ||
1458 (var->green.msb_right != 0) ||
1459 (var->red.msb_right != 0) ||
1460 (var->transp.offset != 0) ||
1461 (var->transp.length != 0))
1462 return -EINVAL;
1463 break;
1464
1465 case 24:
1466 if ((var->blue.offset != 0) ||
1467 (var->green.offset != 8) ||
1468 (var->red.offset != 16) ||
1469 (var->blue.length != 8) ||
1470 (var->green.length != 8) ||
1471 (var->red.length != 8) ||
1472 (var->blue.msb_right != 0) ||
1473 (var->green.msb_right != 0) ||
1474 (var->red.msb_right != 0) ||
1475 !(((var->transp.offset == 0) &&
1476 (var->transp.length == 0)) ||
1477 ((var->transp.offset == 24) &&
1478 (var->transp.length == 8))))
1479 return -EINVAL;
1480 break;
1481
1482 case 32:
1483 /* Figure out if the user meant RGBA or ARGB
1484 and verify the position of the RGB components */
1485
1486 if (var->transp.offset == 24) {
1487 if ((var->blue.offset != 0) ||
1488 (var->green.offset != 8) ||
1489 (var->red.offset != 16))
1490 return -EINVAL;
1491 } else if (var->transp.offset == 0) {
1492 if ((var->blue.offset != 8) ||
1493 (var->green.offset != 16) ||
1494 (var->red.offset != 24))
1495 return -EINVAL;
1496 } else
1497 return -EINVAL;
1498
1499 /* Check the common values for both RGBA and ARGB */
1500
1501 if ((var->blue.length != 8) ||
1502 (var->green.length != 8) ||
1503 (var->red.length != 8) ||
1504 (var->transp.length != 8) ||
1505 (var->blue.msb_right != 0) ||
1506 (var->green.msb_right != 0) ||
1507 (var->red.msb_right != 0))
1508 return -EINVAL;
1509
1510 break;
1511
1512 default:
1513 return -EINVAL;
1514 }
1515
1516 if ((var->xres_virtual <= 0) || (var->yres_virtual <= 0))
1517 return -EINVAL;
1518
1519 if (info->fix.smem_len <
1520 (var->xres_virtual*var->yres_virtual*(var->bits_per_pixel/8)))
1521 return -EINVAL;
1522
1523 if ((var->xres == 0) || (var->yres == 0))
1524 return -EINVAL;
1525
1526 if ((var->xres > MAX(mfd->panel_info.xres,
1527 mfd->panel_info.mode2_xres)) ||
1528 (var->yres > MAX(mfd->panel_info.yres,
1529 mfd->panel_info.mode2_yres)))
1530 return -EINVAL;
1531
1532 if (var->xoffset > (var->xres_virtual - var->xres))
1533 return -EINVAL;
1534
1535 if (var->yoffset > (var->yres_virtual - var->yres))
1536 return -EINVAL;
1537
1538 return 0;
1539}
1540
1541static int msm_fb_set_par(struct fb_info *info)
1542{
1543 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
1544 struct fb_var_screeninfo *var = &info->var;
1545 int old_imgType;
1546 int blank = 0;
1547
1548 old_imgType = mfd->fb_imgType;
1549 switch (var->bits_per_pixel) {
1550 case 16:
1551 if (var->red.offset == 0)
1552 mfd->fb_imgType = MDP_BGR_565;
1553 else
1554 mfd->fb_imgType = MDP_RGB_565;
1555 break;
1556
1557 case 24:
1558 if ((var->transp.offset == 0) && (var->transp.length == 0))
1559 mfd->fb_imgType = MDP_RGB_888;
1560 else if ((var->transp.offset == 24) &&
1561 (var->transp.length == 8)) {
1562 mfd->fb_imgType = MDP_ARGB_8888;
1563 info->var.bits_per_pixel = 32;
1564 }
1565 break;
1566
1567 case 32:
1568 if (var->transp.offset == 24)
1569 mfd->fb_imgType = MDP_ARGB_8888;
1570 else
1571 mfd->fb_imgType = MDP_RGBA_8888;
1572 break;
1573
1574 default:
1575 return -EINVAL;
1576 }
1577
1578 if ((mfd->var_pixclock != var->pixclock) ||
1579 (mfd->hw_refresh && ((mfd->fb_imgType != old_imgType) ||
1580 (mfd->var_pixclock != var->pixclock) ||
1581 (mfd->var_xres != var->xres) ||
1582 (mfd->var_yres != var->yres)))) {
1583 mfd->var_xres = var->xres;
1584 mfd->var_yres = var->yres;
1585 mfd->var_pixclock = var->pixclock;
1586 blank = 1;
1587 }
1588 mfd->fbi->fix.line_length = msm_fb_line_length(mfd->index, var->xres,
1589 var->bits_per_pixel/8);
1590
1591 if (blank) {
1592 msm_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable);
1593 msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable);
1594 }
1595
1596 return 0;
1597}
1598
1599static int msm_fb_stop_sw_refresher(struct msm_fb_data_type *mfd)
1600{
1601 if (mfd->hw_refresh)
1602 return -EPERM;
1603
1604 if (mfd->sw_currently_refreshing) {
1605 down(&mfd->sem);
1606 mfd->sw_currently_refreshing = FALSE;
1607 up(&mfd->sem);
1608
1609 /* wait until the refresher finishes the last job */
1610 wait_for_completion_killable(&mfd->refresher_comp);
1611 }
1612
1613 return 0;
1614}
1615
1616int msm_fb_resume_sw_refresher(struct msm_fb_data_type *mfd)
1617{
1618 boolean do_refresh;
1619
1620 if (mfd->hw_refresh)
1621 return -EPERM;
1622
1623 down(&mfd->sem);
1624 if ((!mfd->sw_currently_refreshing) && (mfd->sw_refreshing_enable)) {
1625 do_refresh = TRUE;
1626 mfd->sw_currently_refreshing = TRUE;
1627 } else {
1628 do_refresh = FALSE;
1629 }
1630 up(&mfd->sem);
1631
1632 if (do_refresh)
1633 mdp_refresh_screen((unsigned long)mfd);
1634
1635 return 0;
1636}
1637
1638#if defined CONFIG_FB_MSM_MDP31
1639static int mdp_blit_split_height(struct fb_info *info,
1640 struct mdp_blit_req *req)
1641{
1642 int ret;
1643 struct mdp_blit_req splitreq;
1644 int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1;
1645 int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1;
1646
1647 splitreq = *req;
1648 /* break dest roi at height*/
1649 d_x_0 = d_x_1 = req->dst_rect.x;
1650 d_w_0 = d_w_1 = req->dst_rect.w;
1651 d_y_0 = req->dst_rect.y;
1652 if (req->dst_rect.h % 32 == 3)
1653 d_h_1 = (req->dst_rect.h - 3) / 2 - 1;
1654 else if (req->dst_rect.h % 32 == 2)
1655 d_h_1 = (req->dst_rect.h - 2) / 2 - 6;
1656 else
1657 d_h_1 = (req->dst_rect.h - 1) / 2 - 1;
1658 d_h_0 = req->dst_rect.h - d_h_1;
1659 d_y_1 = d_y_0 + d_h_0;
1660 if (req->dst_rect.h == 3) {
1661 d_h_1 = 2;
1662 d_h_0 = 2;
1663 d_y_1 = d_y_0 + 1;
1664 }
1665
1666 /* blit first region */
1667 if (((splitreq.flags & 0x07) == 0x04) ||
1668 ((splitreq.flags & 0x07) == 0x0)) {
1669
1670 if (splitreq.flags & MDP_ROT_90) {
1671 s_y_0 = s_y_1 = req->src_rect.y;
1672 s_h_0 = s_h_1 = req->src_rect.h;
1673 s_x_0 = req->src_rect.x;
1674 s_w_1 = (req->src_rect.w * d_h_1) / req->dst_rect.h;
1675 s_w_0 = req->src_rect.w - s_w_1;
1676 s_x_1 = s_x_0 + s_w_0;
1677 if (d_h_1 >= 8 * s_w_1) {
1678 s_w_1++;
1679 s_x_1--;
1680 }
1681 } else {
1682 s_x_0 = s_x_1 = req->src_rect.x;
1683 s_w_0 = s_w_1 = req->src_rect.w;
1684 s_y_0 = req->src_rect.y;
1685 s_h_1 = (req->src_rect.h * d_h_1) / req->dst_rect.h;
1686 s_h_0 = req->src_rect.h - s_h_1;
1687 s_y_1 = s_y_0 + s_h_0;
1688 if (d_h_1 >= 8 * s_h_1) {
1689 s_h_1++;
1690 s_y_1--;
1691 }
1692 }
1693
1694 splitreq.src_rect.h = s_h_0;
1695 splitreq.src_rect.y = s_y_0;
1696 splitreq.dst_rect.h = d_h_0;
1697 splitreq.dst_rect.y = d_y_0;
1698 splitreq.src_rect.x = s_x_0;
1699 splitreq.src_rect.w = s_w_0;
1700 splitreq.dst_rect.x = d_x_0;
1701 splitreq.dst_rect.w = d_w_0;
1702 } else {
1703
1704 if (splitreq.flags & MDP_ROT_90) {
1705 s_y_0 = s_y_1 = req->src_rect.y;
1706 s_h_0 = s_h_1 = req->src_rect.h;
1707 s_x_0 = req->src_rect.x;
1708 s_w_1 = (req->src_rect.w * d_h_0) / req->dst_rect.h;
1709 s_w_0 = req->src_rect.w - s_w_1;
1710 s_x_1 = s_x_0 + s_w_0;
1711 if (d_h_0 >= 8 * s_w_1) {
1712 s_w_1++;
1713 s_x_1--;
1714 }
1715 } else {
1716 s_x_0 = s_x_1 = req->src_rect.x;
1717 s_w_0 = s_w_1 = req->src_rect.w;
1718 s_y_0 = req->src_rect.y;
1719 s_h_1 = (req->src_rect.h * d_h_0) / req->dst_rect.h;
1720 s_h_0 = req->src_rect.h - s_h_1;
1721 s_y_1 = s_y_0 + s_h_0;
1722 if (d_h_0 >= 8 * s_h_1) {
1723 s_h_1++;
1724 s_y_1--;
1725 }
1726 }
1727 splitreq.src_rect.h = s_h_0;
1728 splitreq.src_rect.y = s_y_0;
1729 splitreq.dst_rect.h = d_h_1;
1730 splitreq.dst_rect.y = d_y_1;
1731 splitreq.src_rect.x = s_x_0;
1732 splitreq.src_rect.w = s_w_0;
1733 splitreq.dst_rect.x = d_x_1;
1734 splitreq.dst_rect.w = d_w_1;
1735 }
1736 ret = mdp_ppp_blit(info, &splitreq);
1737 if (ret)
1738 return ret;
1739
1740 /* blit second region */
1741 if (((splitreq.flags & 0x07) == 0x04) ||
1742 ((splitreq.flags & 0x07) == 0x0)) {
1743 splitreq.src_rect.h = s_h_1;
1744 splitreq.src_rect.y = s_y_1;
1745 splitreq.dst_rect.h = d_h_1;
1746 splitreq.dst_rect.y = d_y_1;
1747 splitreq.src_rect.x = s_x_1;
1748 splitreq.src_rect.w = s_w_1;
1749 splitreq.dst_rect.x = d_x_1;
1750 splitreq.dst_rect.w = d_w_1;
1751 } else {
1752 splitreq.src_rect.h = s_h_1;
1753 splitreq.src_rect.y = s_y_1;
1754 splitreq.dst_rect.h = d_h_0;
1755 splitreq.dst_rect.y = d_y_0;
1756 splitreq.src_rect.x = s_x_1;
1757 splitreq.src_rect.w = s_w_1;
1758 splitreq.dst_rect.x = d_x_0;
1759 splitreq.dst_rect.w = d_w_0;
1760 }
1761 ret = mdp_ppp_blit(info, &splitreq);
1762 return ret;
1763}
1764#endif
1765
1766int mdp_blit(struct fb_info *info, struct mdp_blit_req *req)
1767{
1768 int ret;
1769#if defined CONFIG_FB_MSM_MDP31 || defined CONFIG_FB_MSM_MDP30
1770 unsigned int remainder = 0, is_bpp_4 = 0;
1771 struct mdp_blit_req splitreq;
1772 int s_x_0, s_x_1, s_w_0, s_w_1, s_y_0, s_y_1, s_h_0, s_h_1;
1773 int d_x_0, d_x_1, d_w_0, d_w_1, d_y_0, d_y_1, d_h_0, d_h_1;
1774
1775 if (req->flags & MDP_ROT_90) {
1776 if (((req->dst_rect.h == 1) && ((req->src_rect.w != 1) ||
1777 (req->dst_rect.w != req->src_rect.h))) ||
1778 ((req->dst_rect.w == 1) && ((req->src_rect.h != 1) ||
1779 (req->dst_rect.h != req->src_rect.w)))) {
1780 printk(KERN_ERR "mpd_ppp: error scaling when size is 1!\n");
1781 return -EINVAL;
1782 }
1783 } else {
1784 if (((req->dst_rect.w == 1) && ((req->src_rect.w != 1) ||
1785 (req->dst_rect.h != req->src_rect.h))) ||
1786 ((req->dst_rect.h == 1) && ((req->src_rect.h != 1) ||
1787 (req->dst_rect.w != req->src_rect.w)))) {
1788 printk(KERN_ERR "mpd_ppp: error scaling when size is 1!\n");
1789 return -EINVAL;
1790 }
1791 }
1792#endif
1793 if (unlikely(req->src_rect.h == 0 || req->src_rect.w == 0)) {
1794 printk(KERN_ERR "mpd_ppp: src img of zero size!\n");
1795 return -EINVAL;
1796 }
1797 if (unlikely(req->dst_rect.h == 0 || req->dst_rect.w == 0))
1798 return 0;
1799
1800#if defined CONFIG_FB_MSM_MDP31
1801 /* MDP width split workaround */
1802 remainder = (req->dst_rect.w)%32;
1803 ret = mdp_get_bytes_per_pixel(req->dst.format,
1804 (struct msm_fb_data_type *)info->par);
1805 if (ret <= 0) {
1806 printk(KERN_ERR "mdp_ppp: incorrect bpp!\n");
1807 return -EINVAL;
1808 }
1809 is_bpp_4 = (ret == 4) ? 1 : 0;
1810
1811 if ((is_bpp_4 && (remainder == 6 || remainder == 14 ||
1812 remainder == 22 || remainder == 30)) || remainder == 3 ||
1813 (remainder == 1 && req->dst_rect.w != 1) ||
1814 (remainder == 2 && req->dst_rect.w != 2)) {
1815 /* make new request as provide by user */
1816 splitreq = *req;
1817
1818 /* break dest roi at width*/
1819 d_y_0 = d_y_1 = req->dst_rect.y;
1820 d_h_0 = d_h_1 = req->dst_rect.h;
1821 d_x_0 = req->dst_rect.x;
1822
1823 if (remainder == 14)
1824 d_w_1 = (req->dst_rect.w - 14) / 2 + 4;
1825 else if (remainder == 22)
1826 d_w_1 = (req->dst_rect.w - 22) / 2 + 10;
1827 else if (remainder == 30)
1828 d_w_1 = (req->dst_rect.w - 30) / 2 + 10;
1829 else if (remainder == 6)
1830 d_w_1 = req->dst_rect.w / 2 - 1;
1831 else if (remainder == 3)
1832 d_w_1 = (req->dst_rect.w - 3) / 2 - 1;
1833 else if (remainder == 2)
1834 d_w_1 = (req->dst_rect.w - 2) / 2 - 6;
1835 else
1836 d_w_1 = (req->dst_rect.w - 1) / 2 - 1;
1837 d_w_0 = req->dst_rect.w - d_w_1;
1838 d_x_1 = d_x_0 + d_w_0;
1839 if (req->dst_rect.w == 3) {
1840 d_w_1 = 2;
1841 d_w_0 = 2;
1842 d_x_1 = d_x_0 + 1;
1843 }
1844
1845 /* blit first region */
1846 if (((splitreq.flags & 0x07) == 0x07) ||
1847 ((splitreq.flags & 0x07) == 0x0)) {
1848
1849 if (splitreq.flags & MDP_ROT_90) {
1850 s_x_0 = s_x_1 = req->src_rect.x;
1851 s_w_0 = s_w_1 = req->src_rect.w;
1852 s_y_0 = req->src_rect.y;
1853 s_h_1 = (req->src_rect.h * d_w_1) /
1854 req->dst_rect.w;
1855 s_h_0 = req->src_rect.h - s_h_1;
1856 s_y_1 = s_y_0 + s_h_0;
1857 if (d_w_1 >= 8 * s_h_1) {
1858 s_h_1++;
1859 s_y_1--;
1860 }
1861 } else {
1862 s_y_0 = s_y_1 = req->src_rect.y;
1863 s_h_0 = s_h_1 = req->src_rect.h;
1864 s_x_0 = req->src_rect.x;
1865 s_w_1 = (req->src_rect.w * d_w_1) /
1866 req->dst_rect.w;
1867 s_w_0 = req->src_rect.w - s_w_1;
1868 s_x_1 = s_x_0 + s_w_0;
1869 if (d_w_1 >= 8 * s_w_1) {
1870 s_w_1++;
1871 s_x_1--;
1872 }
1873 }
1874
1875 splitreq.src_rect.h = s_h_0;
1876 splitreq.src_rect.y = s_y_0;
1877 splitreq.dst_rect.h = d_h_0;
1878 splitreq.dst_rect.y = d_y_0;
1879 splitreq.src_rect.x = s_x_0;
1880 splitreq.src_rect.w = s_w_0;
1881 splitreq.dst_rect.x = d_x_0;
1882 splitreq.dst_rect.w = d_w_0;
1883 } else {
1884 if (splitreq.flags & MDP_ROT_90) {
1885 s_x_0 = s_x_1 = req->src_rect.x;
1886 s_w_0 = s_w_1 = req->src_rect.w;
1887 s_y_0 = req->src_rect.y;
1888 s_h_1 = (req->src_rect.h * d_w_0) /
1889 req->dst_rect.w;
1890 s_h_0 = req->src_rect.h - s_h_1;
1891 s_y_1 = s_y_0 + s_h_0;
1892 if (d_w_0 >= 8 * s_h_1) {
1893 s_h_1++;
1894 s_y_1--;
1895 }
1896 } else {
1897 s_y_0 = s_y_1 = req->src_rect.y;
1898 s_h_0 = s_h_1 = req->src_rect.h;
1899 s_x_0 = req->src_rect.x;
1900 s_w_1 = (req->src_rect.w * d_w_0) /
1901 req->dst_rect.w;
1902 s_w_0 = req->src_rect.w - s_w_1;
1903 s_x_1 = s_x_0 + s_w_0;
1904 if (d_w_0 >= 8 * s_w_1) {
1905 s_w_1++;
1906 s_x_1--;
1907 }
1908 }
1909 splitreq.src_rect.h = s_h_0;
1910 splitreq.src_rect.y = s_y_0;
1911 splitreq.dst_rect.h = d_h_1;
1912 splitreq.dst_rect.y = d_y_1;
1913 splitreq.src_rect.x = s_x_0;
1914 splitreq.src_rect.w = s_w_0;
1915 splitreq.dst_rect.x = d_x_1;
1916 splitreq.dst_rect.w = d_w_1;
1917 }
1918
1919 if ((splitreq.dst_rect.h % 32 == 3) ||
1920 ((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) ||
1921 ((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2))
1922 ret = mdp_blit_split_height(info, &splitreq);
1923 else
1924 ret = mdp_ppp_blit(info, &splitreq);
1925 if (ret)
1926 return ret;
1927 /* blit second region */
1928 if (((splitreq.flags & 0x07) == 0x07) ||
1929 ((splitreq.flags & 0x07) == 0x0)) {
1930 splitreq.src_rect.h = s_h_1;
1931 splitreq.src_rect.y = s_y_1;
1932 splitreq.dst_rect.h = d_h_1;
1933 splitreq.dst_rect.y = d_y_1;
1934 splitreq.src_rect.x = s_x_1;
1935 splitreq.src_rect.w = s_w_1;
1936 splitreq.dst_rect.x = d_x_1;
1937 splitreq.dst_rect.w = d_w_1;
1938 } else {
1939 splitreq.src_rect.h = s_h_1;
1940 splitreq.src_rect.y = s_y_1;
1941 splitreq.dst_rect.h = d_h_0;
1942 splitreq.dst_rect.y = d_y_0;
1943 splitreq.src_rect.x = s_x_1;
1944 splitreq.src_rect.w = s_w_1;
1945 splitreq.dst_rect.x = d_x_0;
1946 splitreq.dst_rect.w = d_w_0;
1947 }
1948 if (((splitreq.dst_rect.h % 32) == 3) ||
1949 ((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) ||
1950 ((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2))
1951 ret = mdp_blit_split_height(info, &splitreq);
1952 else
1953 ret = mdp_ppp_blit(info, &splitreq);
1954 if (ret)
1955 return ret;
1956 } else if ((req->dst_rect.h % 32) == 3 ||
1957 ((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) ||
1958 ((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2))
1959 ret = mdp_blit_split_height(info, req);
1960 else
1961 ret = mdp_ppp_blit(info, req);
1962 return ret;
1963#elif defined CONFIG_FB_MSM_MDP30
1964 /* MDP width split workaround */
1965 remainder = (req->dst_rect.w)%16;
1966 ret = mdp_get_bytes_per_pixel(req->dst.format,
1967 (struct msm_fb_data_type *)info->par);
1968 if (ret <= 0) {
1969 printk(KERN_ERR "mdp_ppp: incorrect bpp!\n");
1970 return -EINVAL;
1971 }
1972 is_bpp_4 = (ret == 4) ? 1 : 0;
1973
1974 if ((is_bpp_4 && (remainder == 6 || remainder == 14))) {
1975
1976 /* make new request as provide by user */
1977 splitreq = *req;
1978
1979 /* break dest roi at width*/
1980 d_y_0 = d_y_1 = req->dst_rect.y;
1981 d_h_0 = d_h_1 = req->dst_rect.h;
1982 d_x_0 = req->dst_rect.x;
1983
1984 if (remainder == 14 || remainder == 6)
1985 d_w_1 = req->dst_rect.w / 2;
1986 else
1987 d_w_1 = (req->dst_rect.w - 1) / 2 - 1;
1988
1989 d_w_0 = req->dst_rect.w - d_w_1;
1990 d_x_1 = d_x_0 + d_w_0;
1991
1992 /* blit first region */
1993 if (((splitreq.flags & 0x07) == 0x07) ||
1994 ((splitreq.flags & 0x07) == 0x0)) {
1995
1996 if (splitreq.flags & MDP_ROT_90) {
1997 s_x_0 = s_x_1 = req->src_rect.x;
1998 s_w_0 = s_w_1 = req->src_rect.w;
1999 s_y_0 = req->src_rect.y;
2000 s_h_1 = (req->src_rect.h * d_w_1) /
2001 req->dst_rect.w;
2002 s_h_0 = req->src_rect.h - s_h_1;
2003 s_y_1 = s_y_0 + s_h_0;
2004 if (d_w_1 >= 8 * s_h_1) {
2005 s_h_1++;
2006 s_y_1--;
2007 }
2008 } else {
2009 s_y_0 = s_y_1 = req->src_rect.y;
2010 s_h_0 = s_h_1 = req->src_rect.h;
2011 s_x_0 = req->src_rect.x;
2012 s_w_1 = (req->src_rect.w * d_w_1) /
2013 req->dst_rect.w;
2014 s_w_0 = req->src_rect.w - s_w_1;
2015 s_x_1 = s_x_0 + s_w_0;
2016 if (d_w_1 >= 8 * s_w_1) {
2017 s_w_1++;
2018 s_x_1--;
2019 }
2020 }
2021
2022 splitreq.src_rect.h = s_h_0;
2023 splitreq.src_rect.y = s_y_0;
2024 splitreq.dst_rect.h = d_h_0;
2025 splitreq.dst_rect.y = d_y_0;
2026 splitreq.src_rect.x = s_x_0;
2027 splitreq.src_rect.w = s_w_0;
2028 splitreq.dst_rect.x = d_x_0;
2029 splitreq.dst_rect.w = d_w_0;
2030 } else {
2031 if (splitreq.flags & MDP_ROT_90) {
2032 s_x_0 = s_x_1 = req->src_rect.x;
2033 s_w_0 = s_w_1 = req->src_rect.w;
2034 s_y_0 = req->src_rect.y;
2035 s_h_1 = (req->src_rect.h * d_w_0) /
2036 req->dst_rect.w;
2037 s_h_0 = req->src_rect.h - s_h_1;
2038 s_y_1 = s_y_0 + s_h_0;
2039 if (d_w_0 >= 8 * s_h_1) {
2040 s_h_1++;
2041 s_y_1--;
2042 }
2043 } else {
2044 s_y_0 = s_y_1 = req->src_rect.y;
2045 s_h_0 = s_h_1 = req->src_rect.h;
2046 s_x_0 = req->src_rect.x;
2047 s_w_1 = (req->src_rect.w * d_w_0) /
2048 req->dst_rect.w;
2049 s_w_0 = req->src_rect.w - s_w_1;
2050 s_x_1 = s_x_0 + s_w_0;
2051 if (d_w_0 >= 8 * s_w_1) {
2052 s_w_1++;
2053 s_x_1--;
2054 }
2055 }
2056 splitreq.src_rect.h = s_h_0;
2057 splitreq.src_rect.y = s_y_0;
2058 splitreq.dst_rect.h = d_h_1;
2059 splitreq.dst_rect.y = d_y_1;
2060 splitreq.src_rect.x = s_x_0;
2061 splitreq.src_rect.w = s_w_0;
2062 splitreq.dst_rect.x = d_x_1;
2063 splitreq.dst_rect.w = d_w_1;
2064 }
2065
2066 /* No need to split in height */
2067 ret = mdp_ppp_blit(info, &splitreq);
2068
2069 if (ret)
2070 return ret;
2071
2072 /* blit second region */
2073 if (((splitreq.flags & 0x07) == 0x07) ||
2074 ((splitreq.flags & 0x07) == 0x0)) {
2075 splitreq.src_rect.h = s_h_1;
2076 splitreq.src_rect.y = s_y_1;
2077 splitreq.dst_rect.h = d_h_1;
2078 splitreq.dst_rect.y = d_y_1;
2079 splitreq.src_rect.x = s_x_1;
2080 splitreq.src_rect.w = s_w_1;
2081 splitreq.dst_rect.x = d_x_1;
2082 splitreq.dst_rect.w = d_w_1;
2083 } else {
2084 splitreq.src_rect.h = s_h_1;
2085 splitreq.src_rect.y = s_y_1;
2086 splitreq.dst_rect.h = d_h_0;
2087 splitreq.dst_rect.y = d_y_0;
2088 splitreq.src_rect.x = s_x_1;
2089 splitreq.src_rect.w = s_w_1;
2090 splitreq.dst_rect.x = d_x_0;
2091 splitreq.dst_rect.w = d_w_0;
2092 }
2093
2094 /* No need to split in height ... just width */
2095 ret = mdp_ppp_blit(info, &splitreq);
2096
2097 if (ret)
2098 return ret;
2099
2100 } else
2101 ret = mdp_ppp_blit(info, req);
2102 return ret;
2103#else
2104 ret = mdp_ppp_blit(info, req);
2105 return ret;
2106#endif
2107}
2108
2109typedef void (*msm_dma_barrier_function_pointer) (void *, size_t);
2110
2111static inline void msm_fb_dma_barrier_for_rect(struct fb_info *info,
2112 struct mdp_img *img, struct mdp_rect *rect,
2113 msm_dma_barrier_function_pointer dma_barrier_fp
2114 )
2115{
2116 /*
2117 * Compute the start and end addresses of the rectangles.
2118 * NOTE: As currently implemented, the data between
2119 * the end of one row and the start of the next is
2120 * included in the address range rather than
2121 * doing multiple calls for each row.
2122 */
2123 unsigned long start;
2124 size_t size;
2125 char * const pmem_start = info->screen_base;
2126 int bytes_per_pixel = mdp_get_bytes_per_pixel(img->format,
2127 (struct msm_fb_data_type *)info->par);
2128 if (bytes_per_pixel <= 0) {
2129 printk(KERN_ERR "%s incorrect bpp!\n", __func__);
2130 return;
2131 }
2132 start = (unsigned long)pmem_start + img->offset +
2133 (img->width * rect->y + rect->x) * bytes_per_pixel;
2134 size = (rect->h * img->width + rect->w) * bytes_per_pixel;
2135 (*dma_barrier_fp) ((void *) start, size);
2136
2137}
2138
2139static inline void msm_dma_nc_pre(void)
2140{
2141 dmb();
2142}
2143static inline void msm_dma_wt_pre(void)
2144{
2145 dmb();
2146}
2147static inline void msm_dma_todevice_wb_pre(void *start, size_t size)
2148{
2149 dma_cache_pre_ops(start, size, DMA_TO_DEVICE);
2150}
2151
2152static inline void msm_dma_fromdevice_wb_pre(void *start, size_t size)
2153{
2154 dma_cache_pre_ops(start, size, DMA_FROM_DEVICE);
2155}
2156
2157static inline void msm_dma_nc_post(void)
2158{
2159 dmb();
2160}
2161
2162static inline void msm_dma_fromdevice_wt_post(void *start, size_t size)
2163{
2164 dma_cache_post_ops(start, size, DMA_FROM_DEVICE);
2165}
2166
2167static inline void msm_dma_todevice_wb_post(void *start, size_t size)
2168{
2169 dma_cache_post_ops(start, size, DMA_TO_DEVICE);
2170}
2171
2172static inline void msm_dma_fromdevice_wb_post(void *start, size_t size)
2173{
2174 dma_cache_post_ops(start, size, DMA_FROM_DEVICE);
2175}
2176
2177/*
2178 * Do the write barriers required to guarantee data is committed to RAM
2179 * (from CPU cache or internal buffers) before a DMA operation starts.
2180 * NOTE: As currently implemented, the data between
2181 * the end of one row and the start of the next is
2182 * included in the address range rather than
2183 * doing multiple calls for each row.
2184*/
2185static void msm_fb_ensure_memory_coherency_before_dma(struct fb_info *info,
2186 struct mdp_blit_req *req_list,
2187 int req_list_count)
2188{
2189#ifdef CONFIG_ARCH_QSD8X50
2190 int i;
2191
2192 /*
2193 * Normally, do the requested barriers for each address
2194 * range that corresponds to a rectangle.
2195 *
2196 * But if at least one write barrier is requested for data
2197 * going to or from the device but no address range is
2198 * needed for that barrier, then do the barrier, but do it
2199 * only once, no matter how many requests there are.
2200 */
2201 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
2202 switch (mfd->mdp_fb_page_protection) {
2203 default:
2204 case MDP_FB_PAGE_PROTECTION_NONCACHED:
2205 case MDP_FB_PAGE_PROTECTION_WRITECOMBINE:
2206 /*
2207 * The following barrier is only done at most once,
2208 * since further calls would be redundant.
2209 */
2210 for (i = 0; i < req_list_count; i++) {
2211 if (!(req_list[i].flags
2212 & MDP_NO_DMA_BARRIER_START)) {
2213 msm_dma_nc_pre();
2214 break;
2215 }
2216 }
2217 break;
2218
2219 case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE:
2220 /*
2221 * The following barrier is only done at most once,
2222 * since further calls would be redundant.
2223 */
2224 for (i = 0; i < req_list_count; i++) {
2225 if (!(req_list[i].flags
2226 & MDP_NO_DMA_BARRIER_START)) {
2227 msm_dma_wt_pre();
2228 break;
2229 }
2230 }
2231 break;
2232
2233 case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE:
2234 case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE:
2235 for (i = 0; i < req_list_count; i++) {
2236 if (!(req_list[i].flags &
2237 MDP_NO_DMA_BARRIER_START)) {
2238
2239 msm_fb_dma_barrier_for_rect(info,
2240 &(req_list[i].src),
2241 &(req_list[i].src_rect),
2242 msm_dma_todevice_wb_pre
2243 );
2244
2245 msm_fb_dma_barrier_for_rect(info,
2246 &(req_list[i].dst),
2247 &(req_list[i].dst_rect),
2248 msm_dma_todevice_wb_pre
2249 );
2250 }
2251 }
2252 break;
2253 }
2254#else
2255 dmb();
2256#endif
2257}
2258
2259
2260/*
2261 * Do the write barriers required to guarantee data will be re-read from RAM by
2262 * the CPU after a DMA operation ends.
2263 * NOTE: As currently implemented, the data between
2264 * the end of one row and the start of the next is
2265 * included in the address range rather than
2266 * doing multiple calls for each row.
2267*/
2268static void msm_fb_ensure_memory_coherency_after_dma(struct fb_info *info,
2269 struct mdp_blit_req *req_list,
2270 int req_list_count)
2271{
2272#ifdef CONFIG_ARCH_QSD8X50
2273 int i;
2274
2275 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
2276 switch (mfd->mdp_fb_page_protection) {
2277 default:
2278 case MDP_FB_PAGE_PROTECTION_NONCACHED:
2279 case MDP_FB_PAGE_PROTECTION_WRITECOMBINE:
2280 /*
2281 * The following barrier is only done at most once,
2282 * since further calls would be redundant.
2283 */
2284 for (i = 0; i < req_list_count; i++) {
2285 if (!(req_list[i].flags
2286 & MDP_NO_DMA_BARRIER_END)) {
2287 msm_dma_nc_post();
2288 break;
2289 }
2290 }
2291 break;
2292
2293 case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE:
2294 for (i = 0; i < req_list_count; i++) {
2295 if (!(req_list[i].flags &
2296 MDP_NO_DMA_BARRIER_END)) {
2297
2298 msm_fb_dma_barrier_for_rect(info,
2299 &(req_list[i].dst),
2300 &(req_list[i].dst_rect),
2301 msm_dma_fromdevice_wt_post
2302 );
2303 }
2304 }
2305 break;
2306 case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE:
2307 case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE:
2308 for (i = 0; i < req_list_count; i++) {
2309 if (!(req_list[i].flags &
2310 MDP_NO_DMA_BARRIER_END)) {
2311
2312 msm_fb_dma_barrier_for_rect(info,
2313 &(req_list[i].dst),
2314 &(req_list[i].dst_rect),
2315 msm_dma_fromdevice_wb_post
2316 );
2317 }
2318 }
2319 break;
2320 }
2321#else
2322 dmb();
2323#endif
2324}
2325
2326/*
2327 * NOTE: The userspace issues blit operations in a sequence, the sequence
2328 * start with a operation marked START and ends in an operation marked
2329 * END. It is guranteed by the userspace that all the blit operations
2330 * between START and END are only within the regions of areas designated
2331 * by the START and END operations and that the userspace doesnt modify
2332 * those areas. Hence it would be enough to perform barrier/cache operations
2333 * only on the START and END operations.
2334 */
2335static int msmfb_blit(struct fb_info *info, void __user *p)
2336{
2337 /*
2338 * CAUTION: The names of the struct types intentionally *DON'T* match
2339 * the names of the variables declared -- they appear to be swapped.
2340 * Read the code carefully and you should see that the variable names
2341 * make sense.
2342 */
2343 const int MAX_LIST_WINDOW = 16;
2344 struct mdp_blit_req req_list[MAX_LIST_WINDOW];
2345 struct mdp_blit_req_list req_list_header;
2346
2347 int count, i, req_list_count;
2348
2349 /* Get the count size for the total BLIT request. */
2350 if (copy_from_user(&req_list_header, p, sizeof(req_list_header)))
2351 return -EFAULT;
2352 p += sizeof(req_list_header);
2353 count = req_list_header.count;
2354 if (count < 0 || count >= MAX_BLIT_REQ)
2355 return -EINVAL;
2356 while (count > 0) {
2357 /*
2358 * Access the requests through a narrow window to decrease copy
2359 * overhead and make larger requests accessible to the
2360 * coherency management code.
2361 * NOTE: The window size is intended to be larger than the
2362 * typical request size, but not require more than 2
2363 * kbytes of stack storage.
2364 */
2365 req_list_count = count;
2366 if (req_list_count > MAX_LIST_WINDOW)
2367 req_list_count = MAX_LIST_WINDOW;
2368 if (copy_from_user(&req_list, p,
2369 sizeof(struct mdp_blit_req)*req_list_count))
2370 return -EFAULT;
2371
2372 /*
2373 * Ensure that any data CPU may have previously written to
2374 * internal state (but not yet committed to memory) is
2375 * guaranteed to be committed to memory now.
2376 */
2377 msm_fb_ensure_memory_coherency_before_dma(info,
2378 req_list, req_list_count);
2379
2380 /*
2381 * Do the blit DMA, if required -- returning early only if
2382 * there is a failure.
2383 */
2384 for (i = 0; i < req_list_count; i++) {
2385 if (!(req_list[i].flags & MDP_NO_BLIT)) {
2386 /* Do the actual blit. */
2387 int ret = mdp_blit(info, &(req_list[i]));
2388
2389 /*
2390 * Note that early returns don't guarantee
2391 * memory coherency.
2392 */
2393 if (ret)
2394 return ret;
2395 }
2396 }
2397
2398 /*
2399 * Ensure that CPU cache and other internal CPU state is
2400 * updated to reflect any change in memory modified by MDP blit
2401 * DMA.
2402 */
2403 msm_fb_ensure_memory_coherency_after_dma(info,
2404 req_list,
2405 req_list_count);
2406
2407 /* Go to next window of requests. */
2408 count -= req_list_count;
2409 p += sizeof(struct mdp_blit_req)*req_list_count;
2410 }
2411 return 0;
2412}
2413
2414#ifdef CONFIG_FB_MSM_OVERLAY
2415static int msmfb_overlay_get(struct fb_info *info, void __user *p)
2416{
2417 struct mdp_overlay req;
2418 int ret;
2419
2420 if (copy_from_user(&req, p, sizeof(req)))
2421 return -EFAULT;
2422
2423 ret = mdp4_overlay_get(info, &req);
2424 if (ret) {
2425 printk(KERN_ERR "%s: ioctl failed \n",
2426 __func__);
2427 return ret;
2428 }
2429 if (copy_to_user(p, &req, sizeof(req))) {
2430 printk(KERN_ERR "%s: copy2user failed \n",
2431 __func__);
2432 return -EFAULT;
2433 }
2434
2435 return 0;
2436}
2437
2438static int msmfb_overlay_set(struct fb_info *info, void __user *p)
2439{
2440 struct mdp_overlay req;
2441 int ret;
2442
2443 if (copy_from_user(&req, p, sizeof(req)))
2444 return -EFAULT;
2445
2446 ret = mdp4_overlay_set(info, &req);
2447 if (ret) {
2448 printk(KERN_ERR "%s: ioctl failed, rc=%d\n",
2449 __func__, ret);
2450 return ret;
2451 }
2452
2453 if (copy_to_user(p, &req, sizeof(req))) {
2454 printk(KERN_ERR "%s: copy2user failed \n",
2455 __func__);
2456 return -EFAULT;
2457 }
2458
2459 return 0;
2460}
2461
2462static int msmfb_overlay_unset(struct fb_info *info, unsigned long *argp)
2463{
2464 int ret, ndx;
2465
2466 ret = copy_from_user(&ndx, argp, sizeof(ndx));
2467 if (ret) {
2468 printk(KERN_ERR "%s:msmfb_overlay_unset ioctl failed \n",
2469 __func__);
2470 return ret;
2471 }
2472
2473 return mdp4_overlay_unset(info, ndx);
2474}
2475
2476static int msmfb_overlay_play(struct fb_info *info, unsigned long *argp)
2477{
2478 int ret;
2479 struct msmfb_overlay_data req;
2480 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
2481 struct file *p_src_file = 0;
2482 struct file *p_src_plane1_file = 0, *p_src_plane2_file = 0;
2483
2484 if (mfd->overlay_play_enable == 0) /* nothing to do */
2485 return 0;
2486
2487 ret = copy_from_user(&req, argp, sizeof(req));
2488 if (ret) {
2489 printk(KERN_ERR "%s:msmfb_overlay_play ioctl failed \n",
2490 __func__);
2491 return ret;
2492 }
2493
2494 complete(&mfd->msmfb_update_notify);
2495 mutex_lock(&msm_fb_notify_update_sem);
2496 if (mfd->msmfb_no_update_notify_timer.function)
2497 del_timer(&mfd->msmfb_no_update_notify_timer);
2498
2499 mfd->msmfb_no_update_notify_timer.expires =
2500 jiffies + ((1000 * HZ) / 1000);
2501 add_timer(&mfd->msmfb_no_update_notify_timer);
2502 mutex_unlock(&msm_fb_notify_update_sem);
2503
2504 ret = mdp4_overlay_play(info, &req, &p_src_file, &p_src_plane1_file,
2505 &p_src_plane2_file);
2506
2507#ifdef CONFIG_ANDROID_PMEM
2508 if (p_src_file)
2509 put_pmem_file(p_src_file);
2510 if (p_src_plane1_file)
2511 put_pmem_file(p_src_plane1_file);
2512 if (p_src_plane2_file)
2513 put_pmem_file(p_src_plane2_file);
2514#endif
2515
2516 return ret;
2517}
2518
2519static int msmfb_overlay_play_enable(struct fb_info *info, unsigned long *argp)
2520{
2521 int ret, enable;
2522 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
2523
2524 ret = copy_from_user(&enable, argp, sizeof(enable));
2525 if (ret) {
2526 printk(KERN_ERR "%s:msmfb_overlay_play_enable ioctl failed \n",
2527 __func__);
2528 return ret;
2529 }
2530
2531 mfd->overlay_play_enable = enable;
2532
2533 return 0;
2534}
2535
2536
2537#ifdef CONFIG_FB_MSM_OVERLAY_WRITEBACK
2538static int msmfb_overlay_blt(struct fb_info *info, unsigned long *argp)
2539{
2540 int ret;
2541 struct msmfb_overlay_blt req;
2542
2543 ret = copy_from_user(&req, argp, sizeof(req));
2544 if (ret) {
kuogee hsieh405dc302011-07-21 15:06:59 -07002545 pr_err("%s: failed\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002546 return ret;
2547 }
2548
2549 ret = mdp4_overlay_blt(info, &req);
2550
2551 return ret;
2552}
2553
2554static int msmfb_overlay_blt_off(struct fb_info *info, unsigned long *argp)
2555{
2556 int ret;
2557 struct msmfb_overlay_blt req;
2558
kuogee hsieh405dc302011-07-21 15:06:59 -07002559 ret = copy_from_user(&req, argp, sizeof(req));
2560 if (ret) {
2561 pr_err("%s: failed\n", __func__);
2562 return ret;
2563 }
2564
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002565 ret = mdp4_overlay_blt_offset(info, &req);
2566
2567 ret = copy_to_user(argp, &req, sizeof(req));
2568 if (ret)
2569 printk(KERN_ERR "%s:msmfb_overlay_blt_off ioctl failed\n",
2570 __func__);
2571
2572 return ret;
2573}
2574#else
2575static int msmfb_overlay_blt(struct fb_info *info, unsigned long *argp)
2576{
2577 return 0;
2578}
2579static int msmfb_overlay_blt_off(struct fb_info *info, unsigned long *argp)
2580{
2581 return 0;
2582}
2583#endif
2584
kuogee hsieh4aea2742011-07-06 11:05:05 -07002585static int msmfb_overlay_3d_sbys(struct fb_info *info, unsigned long *argp)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002586{
2587 int ret;
2588 struct msmfb_overlay_3d req;
2589
2590 ret = copy_from_user(&req, argp, sizeof(req));
2591 if (ret) {
2592 pr_err("%s:msmfb_overlay_3d_ctrl ioctl failed\n",
2593 __func__);
2594 return ret;
2595 }
2596
kuogee hsieh4aea2742011-07-06 11:05:05 -07002597 ret = mdp4_overlay_3d_sbys(info, &req);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002598
2599 return ret;
2600}
2601
kuogee hsieh405dc302011-07-21 15:06:59 -07002602static int msmfb_mixer_info(struct fb_info *info, unsigned long *argp)
2603{
2604 int ret, cnt;
2605 struct msmfb_mixer_info_req req;
2606
2607 ret = copy_from_user(&req, argp, sizeof(req));
2608 if (ret) {
2609 pr_err("%s: failed\n", __func__);
2610 return ret;
2611 }
2612
2613 cnt = mdp4_mixer_info(req.mixer_num, req.info);
2614 req.cnt = cnt;
2615 ret = copy_to_user(argp, &req, sizeof(req));
2616 if (ret)
2617 pr_err("%s:msmfb_overlay_blt_off ioctl failed\n",
2618 __func__);
2619
2620 return cnt;
2621}
2622
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002623#endif
2624
2625DEFINE_SEMAPHORE(msm_fb_ioctl_ppp_sem);
2626DEFINE_MUTEX(msm_fb_ioctl_lut_sem);
2627DEFINE_MUTEX(msm_fb_ioctl_hist_sem);
2628
2629/* Set color conversion matrix from user space */
2630
2631#ifndef CONFIG_FB_MSM_MDP40
2632static void msmfb_set_color_conv(struct mdp_ccs *p)
2633{
2634 int i;
2635
2636 if (p->direction == MDP_CCS_RGB2YUV) {
2637 /* MDP cmd block enable */
2638 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
2639
2640 /* RGB->YUV primary forward matrix */
2641 for (i = 0; i < MDP_CCS_SIZE; i++)
2642 writel(p->ccs[i], MDP_CSC_PFMVn(i));
2643
2644 #ifdef CONFIG_FB_MSM_MDP31
2645 for (i = 0; i < MDP_BV_SIZE; i++)
2646 writel(p->bv[i], MDP_CSC_POST_BV2n(i));
2647 #endif
2648
2649 /* MDP cmd block disable */
2650 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
2651 } else {
2652 /* MDP cmd block enable */
2653 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
2654
2655 /* YUV->RGB primary reverse matrix */
2656 for (i = 0; i < MDP_CCS_SIZE; i++)
2657 writel(p->ccs[i], MDP_CSC_PRMVn(i));
2658 for (i = 0; i < MDP_BV_SIZE; i++)
2659 writel(p->bv[i], MDP_CSC_PRE_BV1n(i));
2660
2661 /* MDP cmd block disable */
2662 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
2663 }
2664}
2665#endif
2666
2667static int msmfb_notify_update(struct fb_info *info, unsigned long *argp)
2668{
2669 int ret, notify;
2670 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
2671
2672 ret = copy_from_user(&notify, argp, sizeof(int));
2673 if (ret) {
2674 pr_err("%s:ioctl failed\n", __func__);
2675 return ret;
2676 }
2677
2678 if (notify > NOTIFY_UPDATE_STOP)
2679 return -EINVAL;
2680
2681 if (notify == NOTIFY_UPDATE_START) {
2682 INIT_COMPLETION(mfd->msmfb_update_notify);
2683 wait_for_completion_interruptible(&mfd->msmfb_update_notify);
2684 } else {
2685 INIT_COMPLETION(mfd->msmfb_no_update_notify);
2686 wait_for_completion_interruptible(&mfd->msmfb_no_update_notify);
2687 }
2688 return 0;
2689}
2690
2691static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd,
2692 unsigned long arg)
2693{
2694 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
2695 void __user *argp = (void __user *)arg;
2696 struct fb_cursor cursor;
2697 struct fb_cmap cmap;
2698 struct mdp_histogram hist;
2699#ifndef CONFIG_FB_MSM_MDP40
2700 struct mdp_ccs ccs_matrix;
2701#endif
2702 struct mdp_page_protection fb_page_protection;
2703 int ret = 0;
2704
2705 switch (cmd) {
2706#ifdef CONFIG_FB_MSM_OVERLAY
2707 case MSMFB_OVERLAY_GET:
2708 down(&msm_fb_ioctl_ppp_sem);
2709 ret = msmfb_overlay_get(info, argp);
2710 up(&msm_fb_ioctl_ppp_sem);
2711 break;
2712 case MSMFB_OVERLAY_SET:
2713 down(&msm_fb_ioctl_ppp_sem);
2714 ret = msmfb_overlay_set(info, argp);
2715 up(&msm_fb_ioctl_ppp_sem);
2716 break;
2717 case MSMFB_OVERLAY_UNSET:
2718 down(&msm_fb_ioctl_ppp_sem);
2719 ret = msmfb_overlay_unset(info, argp);
2720 up(&msm_fb_ioctl_ppp_sem);
2721 break;
2722 case MSMFB_OVERLAY_PLAY:
2723 down(&msm_fb_ioctl_ppp_sem);
2724 ret = msmfb_overlay_play(info, argp);
2725 up(&msm_fb_ioctl_ppp_sem);
2726 break;
2727 case MSMFB_OVERLAY_PLAY_ENABLE:
2728 down(&msm_fb_ioctl_ppp_sem);
2729 ret = msmfb_overlay_play_enable(info, argp);
2730 up(&msm_fb_ioctl_ppp_sem);
2731 break;
2732 case MSMFB_OVERLAY_BLT:
2733 down(&msm_fb_ioctl_ppp_sem);
2734 ret = msmfb_overlay_blt(info, argp);
2735 up(&msm_fb_ioctl_ppp_sem);
2736 break;
2737 case MSMFB_OVERLAY_BLT_OFFSET:
2738 down(&msm_fb_ioctl_ppp_sem);
2739 ret = msmfb_overlay_blt_off(info, argp);
2740 up(&msm_fb_ioctl_ppp_sem);
2741 break;
2742 case MSMFB_OVERLAY_3D:
2743 down(&msm_fb_ioctl_ppp_sem);
kuogee hsieh4aea2742011-07-06 11:05:05 -07002744 ret = msmfb_overlay_3d_sbys(info, argp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002745 up(&msm_fb_ioctl_ppp_sem);
2746 break;
kuogee hsieh405dc302011-07-21 15:06:59 -07002747 case MSMFB_MIXER_INFO:
2748 down(&msm_fb_ioctl_ppp_sem);
2749 ret = msmfb_mixer_info(info, argp);
2750 up(&msm_fb_ioctl_ppp_sem);
2751 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002752#endif
2753 case MSMFB_BLIT:
2754 down(&msm_fb_ioctl_ppp_sem);
2755 ret = msmfb_blit(info, argp);
2756 up(&msm_fb_ioctl_ppp_sem);
2757
2758 break;
2759
2760 /* Ioctl for setting ccs matrix from user space */
2761 case MSMFB_SET_CCS_MATRIX:
2762#ifndef CONFIG_FB_MSM_MDP40
2763 ret = copy_from_user(&ccs_matrix, argp, sizeof(ccs_matrix));
2764 if (ret) {
2765 printk(KERN_ERR
2766 "%s:MSMFB_SET_CCS_MATRIX ioctl failed \n",
2767 __func__);
2768 return ret;
2769 }
2770
2771 down(&msm_fb_ioctl_ppp_sem);
2772 if (ccs_matrix.direction == MDP_CCS_RGB2YUV)
2773 mdp_ccs_rgb2yuv = ccs_matrix;
2774 else
2775 mdp_ccs_yuv2rgb = ccs_matrix;
2776
2777 msmfb_set_color_conv(&ccs_matrix) ;
2778 up(&msm_fb_ioctl_ppp_sem);
2779#else
2780 ret = -EINVAL;
2781#endif
2782
2783 break;
2784
2785 /* Ioctl for getting ccs matrix to user space */
2786 case MSMFB_GET_CCS_MATRIX:
2787#ifndef CONFIG_FB_MSM_MDP40
2788 ret = copy_from_user(&ccs_matrix, argp, sizeof(ccs_matrix)) ;
2789 if (ret) {
2790 printk(KERN_ERR
2791 "%s:MSMFB_GET_CCS_MATRIX ioctl failed \n",
2792 __func__);
2793 return ret;
2794 }
2795
2796 down(&msm_fb_ioctl_ppp_sem);
2797 if (ccs_matrix.direction == MDP_CCS_RGB2YUV)
2798 ccs_matrix = mdp_ccs_rgb2yuv;
2799 else
2800 ccs_matrix = mdp_ccs_yuv2rgb;
2801
2802 ret = copy_to_user(argp, &ccs_matrix, sizeof(ccs_matrix));
2803
2804 if (ret) {
2805 printk(KERN_ERR
2806 "%s:MSMFB_GET_CCS_MATRIX ioctl failed \n",
2807 __func__);
2808 return ret ;
2809 }
2810 up(&msm_fb_ioctl_ppp_sem);
2811#else
2812 ret = -EINVAL;
2813#endif
2814
2815 break;
2816
2817 case MSMFB_GRP_DISP:
2818#ifdef CONFIG_FB_MSM_MDP22
2819 {
2820 unsigned long grp_id;
2821
2822 ret = copy_from_user(&grp_id, argp, sizeof(grp_id));
2823 if (ret)
2824 return ret;
2825
2826 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
2827 writel(grp_id, MDP_FULL_BYPASS_WORD43);
2828 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF,
2829 FALSE);
2830 break;
2831 }
2832#else
2833 return -EFAULT;
2834#endif
2835 case MSMFB_SUSPEND_SW_REFRESHER:
2836 if (!mfd->panel_power_on)
2837 return -EPERM;
2838
2839 mfd->sw_refreshing_enable = FALSE;
2840 ret = msm_fb_stop_sw_refresher(mfd);
2841 break;
2842
2843 case MSMFB_RESUME_SW_REFRESHER:
2844 if (!mfd->panel_power_on)
2845 return -EPERM;
2846
2847 mfd->sw_refreshing_enable = TRUE;
2848 ret = msm_fb_resume_sw_refresher(mfd);
2849 break;
2850
2851 case MSMFB_CURSOR:
2852 ret = copy_from_user(&cursor, argp, sizeof(cursor));
2853 if (ret)
2854 return ret;
2855
2856 ret = msm_fb_cursor(info, &cursor);
2857 break;
2858
2859 case MSMFB_SET_LUT:
2860 ret = copy_from_user(&cmap, argp, sizeof(cmap));
2861 if (ret)
2862 return ret;
2863
2864 mutex_lock(&msm_fb_ioctl_lut_sem);
2865 ret = msm_fb_set_lut(&cmap, info);
2866 mutex_unlock(&msm_fb_ioctl_lut_sem);
2867 break;
2868
2869 case MSMFB_HISTOGRAM:
2870 if (!mfd->do_histogram)
2871 return -ENODEV;
2872
2873 ret = copy_from_user(&hist, argp, sizeof(hist));
2874 if (ret)
2875 return ret;
2876
2877 mutex_lock(&msm_fb_ioctl_hist_sem);
2878 ret = mfd->do_histogram(info, &hist);
2879 mutex_unlock(&msm_fb_ioctl_hist_sem);
2880 break;
2881
2882 case MSMFB_HISTOGRAM_START:
2883 if (!mfd->do_histogram)
2884 return -ENODEV;
2885 ret = mdp_start_histogram(info);
2886 break;
2887
2888 case MSMFB_HISTOGRAM_STOP:
2889 if (!mfd->do_histogram)
2890 return -ENODEV;
2891 ret = mdp_stop_histogram(info);
2892 break;
2893
2894
2895 case MSMFB_GET_PAGE_PROTECTION:
2896 fb_page_protection.page_protection
2897 = mfd->mdp_fb_page_protection;
2898 ret = copy_to_user(argp, &fb_page_protection,
2899 sizeof(fb_page_protection));
2900 if (ret)
2901 return ret;
2902 break;
2903
2904 case MSMFB_NOTIFY_UPDATE:
2905 ret = msmfb_notify_update(info, argp);
2906 break;
2907
2908 case MSMFB_SET_PAGE_PROTECTION:
2909#if defined CONFIG_ARCH_QSD8X50 || defined CONFIG_ARCH_MSM8X60
2910 ret = copy_from_user(&fb_page_protection, argp,
2911 sizeof(fb_page_protection));
2912 if (ret)
2913 return ret;
2914
2915 /* Validate the proposed page protection settings. */
2916 switch (fb_page_protection.page_protection) {
2917 case MDP_FB_PAGE_PROTECTION_NONCACHED:
2918 case MDP_FB_PAGE_PROTECTION_WRITECOMBINE:
2919 case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE:
2920 /* Write-back cache (read allocate) */
2921 case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE:
2922 /* Write-back cache (write allocate) */
2923 case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE:
2924 mfd->mdp_fb_page_protection =
2925 fb_page_protection.page_protection;
2926 break;
2927 default:
2928 ret = -EINVAL;
2929 break;
2930 }
2931#else
2932 /*
2933 * Don't allow caching until 7k DMA cache operations are
2934 * available.
2935 */
2936 ret = -EINVAL;
2937#endif
2938 break;
2939
2940 default:
2941 MSM_FB_INFO("MDP: unknown ioctl (cmd=%x) received!\n", cmd);
2942 ret = -EINVAL;
2943 break;
2944 }
2945
2946 return ret;
2947}
2948
2949static int msm_fb_register_driver(void)
2950{
2951 return platform_driver_register(&msm_fb_driver);
2952}
2953
2954struct platform_device *msm_fb_add_device(struct platform_device *pdev)
2955{
2956 struct msm_fb_panel_data *pdata;
2957 struct platform_device *this_dev = NULL;
2958 struct fb_info *fbi;
2959 struct msm_fb_data_type *mfd = NULL;
2960 u32 type, id, fb_num;
2961
2962 if (!pdev)
2963 return NULL;
2964 id = pdev->id;
2965
2966 pdata = pdev->dev.platform_data;
2967 if (!pdata)
2968 return NULL;
2969 type = pdata->panel_info.type;
2970
2971#if defined MSM_FB_NUM
2972 /*
2973 * over written fb_num which defined
2974 * at panel_info
2975 *
2976 */
2977 if (type == HDMI_PANEL || type == DTV_PANEL || type == TV_PANEL)
2978 pdata->panel_info.fb_num = 1;
2979 else
2980 pdata->panel_info.fb_num = MSM_FB_NUM;
2981
2982 MSM_FB_INFO("setting pdata->panel_info.fb_num to %d. type: %d\n",
2983 pdata->panel_info.fb_num, type);
2984#endif
2985 fb_num = pdata->panel_info.fb_num;
2986
2987 if (fb_num <= 0)
2988 return NULL;
2989
2990 if (fbi_list_index >= MAX_FBI_LIST) {
2991 printk(KERN_ERR "msm_fb: no more framebuffer info list!\n");
2992 return NULL;
2993 }
2994 /*
2995 * alloc panel device data
2996 */
2997 this_dev = msm_fb_device_alloc(pdata, type, id);
2998
2999 if (!this_dev) {
3000 printk(KERN_ERR
3001 "%s: msm_fb_device_alloc failed!\n", __func__);
3002 return NULL;
3003 }
3004
3005 /*
3006 * alloc framebuffer info + par data
3007 */
3008 fbi = framebuffer_alloc(sizeof(struct msm_fb_data_type), NULL);
3009 if (fbi == NULL) {
3010 platform_device_put(this_dev);
3011 printk(KERN_ERR "msm_fb: can't alloca framebuffer info data!\n");
3012 return NULL;
3013 }
3014
3015 mfd = (struct msm_fb_data_type *)fbi->par;
3016 mfd->key = MFD_KEY;
3017 mfd->fbi = fbi;
3018 mfd->panel.type = type;
3019 mfd->panel.id = id;
3020 mfd->fb_page = fb_num;
3021 mfd->index = fbi_list_index;
3022 mfd->mdp_fb_page_protection = MDP_FB_PAGE_PROTECTION_WRITECOMBINE;
3023
3024 /* link to the latest pdev */
3025 mfd->pdev = this_dev;
3026
3027 mfd_list[mfd_list_index++] = mfd;
3028 fbi_list[fbi_list_index++] = fbi;
3029
3030 /*
3031 * set driver data
3032 */
3033 platform_set_drvdata(this_dev, mfd);
3034
3035 if (platform_device_add(this_dev)) {
3036 printk(KERN_ERR "msm_fb: platform_device_add failed!\n");
3037 platform_device_put(this_dev);
3038 framebuffer_release(fbi);
3039 fbi_list_index--;
3040 return NULL;
3041 }
3042 return this_dev;
3043}
3044EXPORT_SYMBOL(msm_fb_add_device);
3045
3046int get_fb_phys_info(unsigned long *start, unsigned long *len, int fb_num)
3047{
3048 struct fb_info *info;
3049
3050 if (fb_num > MAX_FBI_LIST)
3051 return -1;
3052
3053 info = fbi_list[fb_num];
3054 if (!info)
3055 return -1;
3056
3057 *start = info->fix.smem_start;
3058 *len = info->fix.smem_len;
3059 return 0;
3060}
3061EXPORT_SYMBOL(get_fb_phys_info);
3062
3063int __init msm_fb_init(void)
3064{
3065 int rc = -ENODEV;
3066
3067 if (msm_fb_register_driver())
3068 return rc;
3069
3070#ifdef MSM_FB_ENABLE_DBGFS
3071 {
3072 struct dentry *root;
3073
3074 if ((root = msm_fb_get_debugfs_root()) != NULL) {
3075 msm_fb_debugfs_file_create(root,
3076 "msm_fb_msg_printing_level",
3077 (u32 *) &msm_fb_msg_level);
3078 msm_fb_debugfs_file_create(root,
3079 "mddi_msg_printing_level",
3080 (u32 *) &mddi_msg_level);
3081 msm_fb_debugfs_file_create(root, "msm_fb_debug_enabled",
3082 (u32 *) &msm_fb_debug_enabled);
3083 }
3084 }
3085#endif
3086
3087 return 0;
3088}
3089
3090module_init(msm_fb_init);