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