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