blob: b5cd46a3a50137a8091c271465fab9ed2d61d21f [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;
306 fbram = ioremap((unsigned long)fbram_phys, fbram_size);
307
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) {
2545 printk(KERN_ERR "%s:msmfb_overlay_blt ioctl failed\n",
2546 __func__);
2547 return ret;
2548 }
2549
2550 ret = mdp4_overlay_blt(info, &req);
2551
2552 return ret;
2553}
2554
2555static int msmfb_overlay_blt_off(struct fb_info *info, unsigned long *argp)
2556{
2557 int ret;
2558 struct msmfb_overlay_blt req;
2559
2560 ret = mdp4_overlay_blt_offset(info, &req);
2561
2562 ret = copy_to_user(argp, &req, sizeof(req));
2563 if (ret)
2564 printk(KERN_ERR "%s:msmfb_overlay_blt_off ioctl failed\n",
2565 __func__);
2566
2567 return ret;
2568}
2569#else
2570static int msmfb_overlay_blt(struct fb_info *info, unsigned long *argp)
2571{
2572 return 0;
2573}
2574static int msmfb_overlay_blt_off(struct fb_info *info, unsigned long *argp)
2575{
2576 return 0;
2577}
2578#endif
2579
kuogee hsieh4aea2742011-07-06 11:05:05 -07002580static int msmfb_overlay_3d_sbys(struct fb_info *info, unsigned long *argp)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002581{
2582 int ret;
2583 struct msmfb_overlay_3d req;
2584
2585 ret = copy_from_user(&req, argp, sizeof(req));
2586 if (ret) {
2587 pr_err("%s:msmfb_overlay_3d_ctrl ioctl failed\n",
2588 __func__);
2589 return ret;
2590 }
2591
kuogee hsieh4aea2742011-07-06 11:05:05 -07002592 ret = mdp4_overlay_3d_sbys(info, &req);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002593
2594 return ret;
2595}
2596
2597#endif
2598
2599DEFINE_SEMAPHORE(msm_fb_ioctl_ppp_sem);
2600DEFINE_MUTEX(msm_fb_ioctl_lut_sem);
2601DEFINE_MUTEX(msm_fb_ioctl_hist_sem);
2602
2603/* Set color conversion matrix from user space */
2604
2605#ifndef CONFIG_FB_MSM_MDP40
2606static void msmfb_set_color_conv(struct mdp_ccs *p)
2607{
2608 int i;
2609
2610 if (p->direction == MDP_CCS_RGB2YUV) {
2611 /* MDP cmd block enable */
2612 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
2613
2614 /* RGB->YUV primary forward matrix */
2615 for (i = 0; i < MDP_CCS_SIZE; i++)
2616 writel(p->ccs[i], MDP_CSC_PFMVn(i));
2617
2618 #ifdef CONFIG_FB_MSM_MDP31
2619 for (i = 0; i < MDP_BV_SIZE; i++)
2620 writel(p->bv[i], MDP_CSC_POST_BV2n(i));
2621 #endif
2622
2623 /* MDP cmd block disable */
2624 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
2625 } else {
2626 /* MDP cmd block enable */
2627 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
2628
2629 /* YUV->RGB primary reverse matrix */
2630 for (i = 0; i < MDP_CCS_SIZE; i++)
2631 writel(p->ccs[i], MDP_CSC_PRMVn(i));
2632 for (i = 0; i < MDP_BV_SIZE; i++)
2633 writel(p->bv[i], MDP_CSC_PRE_BV1n(i));
2634
2635 /* MDP cmd block disable */
2636 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
2637 }
2638}
2639#endif
2640
2641static int msmfb_notify_update(struct fb_info *info, unsigned long *argp)
2642{
2643 int ret, notify;
2644 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
2645
2646 ret = copy_from_user(&notify, argp, sizeof(int));
2647 if (ret) {
2648 pr_err("%s:ioctl failed\n", __func__);
2649 return ret;
2650 }
2651
2652 if (notify > NOTIFY_UPDATE_STOP)
2653 return -EINVAL;
2654
2655 if (notify == NOTIFY_UPDATE_START) {
2656 INIT_COMPLETION(mfd->msmfb_update_notify);
2657 wait_for_completion_interruptible(&mfd->msmfb_update_notify);
2658 } else {
2659 INIT_COMPLETION(mfd->msmfb_no_update_notify);
2660 wait_for_completion_interruptible(&mfd->msmfb_no_update_notify);
2661 }
2662 return 0;
2663}
2664
2665static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd,
2666 unsigned long arg)
2667{
2668 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
2669 void __user *argp = (void __user *)arg;
2670 struct fb_cursor cursor;
2671 struct fb_cmap cmap;
2672 struct mdp_histogram hist;
2673#ifndef CONFIG_FB_MSM_MDP40
2674 struct mdp_ccs ccs_matrix;
2675#endif
2676 struct mdp_page_protection fb_page_protection;
2677 int ret = 0;
2678
2679 switch (cmd) {
2680#ifdef CONFIG_FB_MSM_OVERLAY
2681 case MSMFB_OVERLAY_GET:
2682 down(&msm_fb_ioctl_ppp_sem);
2683 ret = msmfb_overlay_get(info, argp);
2684 up(&msm_fb_ioctl_ppp_sem);
2685 break;
2686 case MSMFB_OVERLAY_SET:
2687 down(&msm_fb_ioctl_ppp_sem);
2688 ret = msmfb_overlay_set(info, argp);
2689 up(&msm_fb_ioctl_ppp_sem);
2690 break;
2691 case MSMFB_OVERLAY_UNSET:
2692 down(&msm_fb_ioctl_ppp_sem);
2693 ret = msmfb_overlay_unset(info, argp);
2694 up(&msm_fb_ioctl_ppp_sem);
2695 break;
2696 case MSMFB_OVERLAY_PLAY:
2697 down(&msm_fb_ioctl_ppp_sem);
2698 ret = msmfb_overlay_play(info, argp);
2699 up(&msm_fb_ioctl_ppp_sem);
2700 break;
2701 case MSMFB_OVERLAY_PLAY_ENABLE:
2702 down(&msm_fb_ioctl_ppp_sem);
2703 ret = msmfb_overlay_play_enable(info, argp);
2704 up(&msm_fb_ioctl_ppp_sem);
2705 break;
2706 case MSMFB_OVERLAY_BLT:
2707 down(&msm_fb_ioctl_ppp_sem);
2708 ret = msmfb_overlay_blt(info, argp);
2709 up(&msm_fb_ioctl_ppp_sem);
2710 break;
2711 case MSMFB_OVERLAY_BLT_OFFSET:
2712 down(&msm_fb_ioctl_ppp_sem);
2713 ret = msmfb_overlay_blt_off(info, argp);
2714 up(&msm_fb_ioctl_ppp_sem);
2715 break;
2716 case MSMFB_OVERLAY_3D:
2717 down(&msm_fb_ioctl_ppp_sem);
kuogee hsieh4aea2742011-07-06 11:05:05 -07002718 ret = msmfb_overlay_3d_sbys(info, argp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002719 up(&msm_fb_ioctl_ppp_sem);
2720 break;
2721#endif
2722 case MSMFB_BLIT:
2723 down(&msm_fb_ioctl_ppp_sem);
2724 ret = msmfb_blit(info, argp);
2725 up(&msm_fb_ioctl_ppp_sem);
2726
2727 break;
2728
2729 /* Ioctl for setting ccs matrix from user space */
2730 case MSMFB_SET_CCS_MATRIX:
2731#ifndef CONFIG_FB_MSM_MDP40
2732 ret = copy_from_user(&ccs_matrix, argp, sizeof(ccs_matrix));
2733 if (ret) {
2734 printk(KERN_ERR
2735 "%s:MSMFB_SET_CCS_MATRIX ioctl failed \n",
2736 __func__);
2737 return ret;
2738 }
2739
2740 down(&msm_fb_ioctl_ppp_sem);
2741 if (ccs_matrix.direction == MDP_CCS_RGB2YUV)
2742 mdp_ccs_rgb2yuv = ccs_matrix;
2743 else
2744 mdp_ccs_yuv2rgb = ccs_matrix;
2745
2746 msmfb_set_color_conv(&ccs_matrix) ;
2747 up(&msm_fb_ioctl_ppp_sem);
2748#else
2749 ret = -EINVAL;
2750#endif
2751
2752 break;
2753
2754 /* Ioctl for getting ccs matrix to user space */
2755 case MSMFB_GET_CCS_MATRIX:
2756#ifndef CONFIG_FB_MSM_MDP40
2757 ret = copy_from_user(&ccs_matrix, argp, sizeof(ccs_matrix)) ;
2758 if (ret) {
2759 printk(KERN_ERR
2760 "%s:MSMFB_GET_CCS_MATRIX ioctl failed \n",
2761 __func__);
2762 return ret;
2763 }
2764
2765 down(&msm_fb_ioctl_ppp_sem);
2766 if (ccs_matrix.direction == MDP_CCS_RGB2YUV)
2767 ccs_matrix = mdp_ccs_rgb2yuv;
2768 else
2769 ccs_matrix = mdp_ccs_yuv2rgb;
2770
2771 ret = copy_to_user(argp, &ccs_matrix, sizeof(ccs_matrix));
2772
2773 if (ret) {
2774 printk(KERN_ERR
2775 "%s:MSMFB_GET_CCS_MATRIX ioctl failed \n",
2776 __func__);
2777 return ret ;
2778 }
2779 up(&msm_fb_ioctl_ppp_sem);
2780#else
2781 ret = -EINVAL;
2782#endif
2783
2784 break;
2785
2786 case MSMFB_GRP_DISP:
2787#ifdef CONFIG_FB_MSM_MDP22
2788 {
2789 unsigned long grp_id;
2790
2791 ret = copy_from_user(&grp_id, argp, sizeof(grp_id));
2792 if (ret)
2793 return ret;
2794
2795 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
2796 writel(grp_id, MDP_FULL_BYPASS_WORD43);
2797 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF,
2798 FALSE);
2799 break;
2800 }
2801#else
2802 return -EFAULT;
2803#endif
2804 case MSMFB_SUSPEND_SW_REFRESHER:
2805 if (!mfd->panel_power_on)
2806 return -EPERM;
2807
2808 mfd->sw_refreshing_enable = FALSE;
2809 ret = msm_fb_stop_sw_refresher(mfd);
2810 break;
2811
2812 case MSMFB_RESUME_SW_REFRESHER:
2813 if (!mfd->panel_power_on)
2814 return -EPERM;
2815
2816 mfd->sw_refreshing_enable = TRUE;
2817 ret = msm_fb_resume_sw_refresher(mfd);
2818 break;
2819
2820 case MSMFB_CURSOR:
2821 ret = copy_from_user(&cursor, argp, sizeof(cursor));
2822 if (ret)
2823 return ret;
2824
2825 ret = msm_fb_cursor(info, &cursor);
2826 break;
2827
2828 case MSMFB_SET_LUT:
2829 ret = copy_from_user(&cmap, argp, sizeof(cmap));
2830 if (ret)
2831 return ret;
2832
2833 mutex_lock(&msm_fb_ioctl_lut_sem);
2834 ret = msm_fb_set_lut(&cmap, info);
2835 mutex_unlock(&msm_fb_ioctl_lut_sem);
2836 break;
2837
2838 case MSMFB_HISTOGRAM:
2839 if (!mfd->do_histogram)
2840 return -ENODEV;
2841
2842 ret = copy_from_user(&hist, argp, sizeof(hist));
2843 if (ret)
2844 return ret;
2845
2846 mutex_lock(&msm_fb_ioctl_hist_sem);
2847 ret = mfd->do_histogram(info, &hist);
2848 mutex_unlock(&msm_fb_ioctl_hist_sem);
2849 break;
2850
2851 case MSMFB_HISTOGRAM_START:
2852 if (!mfd->do_histogram)
2853 return -ENODEV;
2854 ret = mdp_start_histogram(info);
2855 break;
2856
2857 case MSMFB_HISTOGRAM_STOP:
2858 if (!mfd->do_histogram)
2859 return -ENODEV;
2860 ret = mdp_stop_histogram(info);
2861 break;
2862
2863
2864 case MSMFB_GET_PAGE_PROTECTION:
2865 fb_page_protection.page_protection
2866 = mfd->mdp_fb_page_protection;
2867 ret = copy_to_user(argp, &fb_page_protection,
2868 sizeof(fb_page_protection));
2869 if (ret)
2870 return ret;
2871 break;
2872
2873 case MSMFB_NOTIFY_UPDATE:
2874 ret = msmfb_notify_update(info, argp);
2875 break;
2876
2877 case MSMFB_SET_PAGE_PROTECTION:
2878#if defined CONFIG_ARCH_QSD8X50 || defined CONFIG_ARCH_MSM8X60
2879 ret = copy_from_user(&fb_page_protection, argp,
2880 sizeof(fb_page_protection));
2881 if (ret)
2882 return ret;
2883
2884 /* Validate the proposed page protection settings. */
2885 switch (fb_page_protection.page_protection) {
2886 case MDP_FB_PAGE_PROTECTION_NONCACHED:
2887 case MDP_FB_PAGE_PROTECTION_WRITECOMBINE:
2888 case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE:
2889 /* Write-back cache (read allocate) */
2890 case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE:
2891 /* Write-back cache (write allocate) */
2892 case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE:
2893 mfd->mdp_fb_page_protection =
2894 fb_page_protection.page_protection;
2895 break;
2896 default:
2897 ret = -EINVAL;
2898 break;
2899 }
2900#else
2901 /*
2902 * Don't allow caching until 7k DMA cache operations are
2903 * available.
2904 */
2905 ret = -EINVAL;
2906#endif
2907 break;
2908
2909 default:
2910 MSM_FB_INFO("MDP: unknown ioctl (cmd=%x) received!\n", cmd);
2911 ret = -EINVAL;
2912 break;
2913 }
2914
2915 return ret;
2916}
2917
2918static int msm_fb_register_driver(void)
2919{
2920 return platform_driver_register(&msm_fb_driver);
2921}
2922
2923struct platform_device *msm_fb_add_device(struct platform_device *pdev)
2924{
2925 struct msm_fb_panel_data *pdata;
2926 struct platform_device *this_dev = NULL;
2927 struct fb_info *fbi;
2928 struct msm_fb_data_type *mfd = NULL;
2929 u32 type, id, fb_num;
2930
2931 if (!pdev)
2932 return NULL;
2933 id = pdev->id;
2934
2935 pdata = pdev->dev.platform_data;
2936 if (!pdata)
2937 return NULL;
2938 type = pdata->panel_info.type;
2939
2940#if defined MSM_FB_NUM
2941 /*
2942 * over written fb_num which defined
2943 * at panel_info
2944 *
2945 */
2946 if (type == HDMI_PANEL || type == DTV_PANEL || type == TV_PANEL)
2947 pdata->panel_info.fb_num = 1;
2948 else
2949 pdata->panel_info.fb_num = MSM_FB_NUM;
2950
2951 MSM_FB_INFO("setting pdata->panel_info.fb_num to %d. type: %d\n",
2952 pdata->panel_info.fb_num, type);
2953#endif
2954 fb_num = pdata->panel_info.fb_num;
2955
2956 if (fb_num <= 0)
2957 return NULL;
2958
2959 if (fbi_list_index >= MAX_FBI_LIST) {
2960 printk(KERN_ERR "msm_fb: no more framebuffer info list!\n");
2961 return NULL;
2962 }
2963 /*
2964 * alloc panel device data
2965 */
2966 this_dev = msm_fb_device_alloc(pdata, type, id);
2967
2968 if (!this_dev) {
2969 printk(KERN_ERR
2970 "%s: msm_fb_device_alloc failed!\n", __func__);
2971 return NULL;
2972 }
2973
2974 /*
2975 * alloc framebuffer info + par data
2976 */
2977 fbi = framebuffer_alloc(sizeof(struct msm_fb_data_type), NULL);
2978 if (fbi == NULL) {
2979 platform_device_put(this_dev);
2980 printk(KERN_ERR "msm_fb: can't alloca framebuffer info data!\n");
2981 return NULL;
2982 }
2983
2984 mfd = (struct msm_fb_data_type *)fbi->par;
2985 mfd->key = MFD_KEY;
2986 mfd->fbi = fbi;
2987 mfd->panel.type = type;
2988 mfd->panel.id = id;
2989 mfd->fb_page = fb_num;
2990 mfd->index = fbi_list_index;
2991 mfd->mdp_fb_page_protection = MDP_FB_PAGE_PROTECTION_WRITECOMBINE;
2992
2993 /* link to the latest pdev */
2994 mfd->pdev = this_dev;
2995
2996 mfd_list[mfd_list_index++] = mfd;
2997 fbi_list[fbi_list_index++] = fbi;
2998
2999 /*
3000 * set driver data
3001 */
3002 platform_set_drvdata(this_dev, mfd);
3003
3004 if (platform_device_add(this_dev)) {
3005 printk(KERN_ERR "msm_fb: platform_device_add failed!\n");
3006 platform_device_put(this_dev);
3007 framebuffer_release(fbi);
3008 fbi_list_index--;
3009 return NULL;
3010 }
3011 return this_dev;
3012}
3013EXPORT_SYMBOL(msm_fb_add_device);
3014
3015int get_fb_phys_info(unsigned long *start, unsigned long *len, int fb_num)
3016{
3017 struct fb_info *info;
3018
3019 if (fb_num > MAX_FBI_LIST)
3020 return -1;
3021
3022 info = fbi_list[fb_num];
3023 if (!info)
3024 return -1;
3025
3026 *start = info->fix.smem_start;
3027 *len = info->fix.smem_len;
3028 return 0;
3029}
3030EXPORT_SYMBOL(get_fb_phys_info);
3031
3032int __init msm_fb_init(void)
3033{
3034 int rc = -ENODEV;
3035
3036 if (msm_fb_register_driver())
3037 return rc;
3038
3039#ifdef MSM_FB_ENABLE_DBGFS
3040 {
3041 struct dentry *root;
3042
3043 if ((root = msm_fb_get_debugfs_root()) != NULL) {
3044 msm_fb_debugfs_file_create(root,
3045 "msm_fb_msg_printing_level",
3046 (u32 *) &msm_fb_msg_level);
3047 msm_fb_debugfs_file_create(root,
3048 "mddi_msg_printing_level",
3049 (u32 *) &mddi_msg_level);
3050 msm_fb_debugfs_file_create(root, "msm_fb_debug_enabled",
3051 (u32 *) &msm_fb_debug_enabled);
3052 }
3053 }
3054#endif
3055
3056 return 0;
3057}
3058
3059module_init(msm_fb_init);