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