blob: ab91e41afa7d5fc9918e3c921254a9d116cd8e33 [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
Ravishangar Kalyanam6a75b862011-12-15 10:33:39 -0800668static int unset_bl_level, bl_updated;
669static int bl_level_old;
670
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700671void msm_fb_set_backlight(struct msm_fb_data_type *mfd, __u32 bkl_lvl)
672{
673 struct msm_fb_panel_data *pdata;
674
Ravishangar Kalyanam6a75b862011-12-15 10:33:39 -0800675 if (!mfd->panel_power_on || !bl_updated) {
676 unset_bl_level = bkl_lvl;
677 return;
678 } else {
679 unset_bl_level = 0;
680 }
681
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700682 pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
683
684 if ((pdata) && (pdata->set_backlight)) {
685 down(&mfd->sem);
Ravishangar Kalyanam6a75b862011-12-15 10:33:39 -0800686 if (bl_level_old == bkl_lvl) {
687 up(&mfd->sem);
688 return;
689 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700690 mfd->bl_level = bkl_lvl;
691 pdata->set_backlight(mfd);
Ravishangar Kalyanam6a75b862011-12-15 10:33:39 -0800692 bl_level_old = mfd->bl_level;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700693 up(&mfd->sem);
694 }
695}
696
697static int msm_fb_blank_sub(int blank_mode, struct fb_info *info,
698 boolean op_enable)
699{
700 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
701 struct msm_fb_panel_data *pdata = NULL;
702 int ret = 0;
703
704 if (!op_enable)
705 return -EPERM;
706
707 pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data;
708 if ((!pdata) || (!pdata->on) || (!pdata->off)) {
709 printk(KERN_ERR "msm_fb_blank_sub: no panel operation detected!\n");
710 return -ENODEV;
711 }
712
713 switch (blank_mode) {
714 case FB_BLANK_UNBLANK:
715 if (!mfd->panel_power_on) {
716 msleep(16);
717 ret = pdata->on(mfd->pdev);
718 if (ret == 0) {
719 mfd->panel_power_on = TRUE;
720
721/* ToDo: possible conflict with android which doesn't expect sw refresher */
722/*
723 if (!mfd->hw_refresh)
724 {
725 if ((ret = msm_fb_resume_sw_refresher(mfd)) != 0)
726 {
727 MSM_FB_INFO("msm_fb_blank_sub: msm_fb_resume_sw_refresher failed = %d!\n",ret);
728 }
729 }
730*/
731 }
732 }
733 break;
734
735 case FB_BLANK_VSYNC_SUSPEND:
736 case FB_BLANK_HSYNC_SUSPEND:
737 case FB_BLANK_NORMAL:
738 case FB_BLANK_POWERDOWN:
739 default:
740 if (mfd->panel_power_on) {
741 int curr_pwr_state;
742
743 mfd->op_enable = FALSE;
744 curr_pwr_state = mfd->panel_power_on;
745 mfd->panel_power_on = FALSE;
Ravishangar Kalyanam6a75b862011-12-15 10:33:39 -0800746 bl_updated = 0;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -0700747
748 msleep(16);
749 ret = pdata->off(mfd->pdev);
750 if (ret)
751 mfd->panel_power_on = curr_pwr_state;
752
753 mfd->op_enable = TRUE;
754 }
755 break;
756 }
757
758 return ret;
759}
760
761static void msm_fb_fillrect(struct fb_info *info,
762 const struct fb_fillrect *rect)
763{
764 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
765
766 cfb_fillrect(info, rect);
767 if (!mfd->hw_refresh && (info->var.yoffset == 0) &&
768 !mfd->sw_currently_refreshing) {
769 struct fb_var_screeninfo var;
770
771 var = info->var;
772 var.reserved[0] = 0x54445055;
773 var.reserved[1] = (rect->dy << 16) | (rect->dx);
774 var.reserved[2] = ((rect->dy + rect->height) << 16) |
775 (rect->dx + rect->width);
776
777 msm_fb_pan_display(&var, info);
778 }
779}
780
781static void msm_fb_copyarea(struct fb_info *info,
782 const struct fb_copyarea *area)
783{
784 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
785
786 cfb_copyarea(info, area);
787 if (!mfd->hw_refresh && (info->var.yoffset == 0) &&
788 !mfd->sw_currently_refreshing) {
789 struct fb_var_screeninfo var;
790
791 var = info->var;
792 var.reserved[0] = 0x54445055;
793 var.reserved[1] = (area->dy << 16) | (area->dx);
794 var.reserved[2] = ((area->dy + area->height) << 16) |
795 (area->dx + area->width);
796
797 msm_fb_pan_display(&var, info);
798 }
799}
800
801static void msm_fb_imageblit(struct fb_info *info, const struct fb_image *image)
802{
803 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
804
805 cfb_imageblit(info, image);
806 if (!mfd->hw_refresh && (info->var.yoffset == 0) &&
807 !mfd->sw_currently_refreshing) {
808 struct fb_var_screeninfo var;
809
810 var = info->var;
811 var.reserved[0] = 0x54445055;
812 var.reserved[1] = (image->dy << 16) | (image->dx);
813 var.reserved[2] = ((image->dy + image->height) << 16) |
814 (image->dx + image->width);
815
816 msm_fb_pan_display(&var, info);
817 }
818}
819
820static int msm_fb_blank(int blank_mode, struct fb_info *info)
821{
822 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
823 return msm_fb_blank_sub(blank_mode, info, mfd->op_enable);
824}
825
826static int msm_fb_set_lut(struct fb_cmap *cmap, struct fb_info *info)
827{
828 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
829
830 if (!mfd->lut_update)
831 return -ENODEV;
832
833 mfd->lut_update(info, cmap);
834 return 0;
835}
836
837/*
838 * Custom Framebuffer mmap() function for MSM driver.
839 * Differs from standard mmap() function by allowing for customized
840 * page-protection.
841 */
842static int msm_fb_mmap(struct fb_info *info, struct vm_area_struct * vma)
843{
844 /* Get frame buffer memory range. */
845 unsigned long start = info->fix.smem_start;
846 u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len);
847 unsigned long off = vma->vm_pgoff << PAGE_SHIFT;
848 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
849 if (off >= len) {
850 /* memory mapped io */
851 off -= len;
852 if (info->var.accel_flags) {
853 mutex_unlock(&info->lock);
854 return -EINVAL;
855 }
856 start = info->fix.mmio_start;
857 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len);
858 }
859
860 /* Set VM flags. */
861 start &= PAGE_MASK;
862 if ((vma->vm_end - vma->vm_start + off) > len)
863 return -EINVAL;
864 off += start;
865 vma->vm_pgoff = off >> PAGE_SHIFT;
866 /* This is an IO map - tell maydump to skip this VMA */
867 vma->vm_flags |= VM_IO | VM_RESERVED;
868
869 /* Set VM page protection */
870 if (mfd->mdp_fb_page_protection == MDP_FB_PAGE_PROTECTION_WRITECOMBINE)
871 vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
872 else if (mfd->mdp_fb_page_protection ==
873 MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE)
874 vma->vm_page_prot = pgprot_writethroughcache(vma->vm_page_prot);
875 else if (mfd->mdp_fb_page_protection ==
876 MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE)
877 vma->vm_page_prot = pgprot_writebackcache(vma->vm_page_prot);
878 else if (mfd->mdp_fb_page_protection ==
879 MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE)
880 vma->vm_page_prot = pgprot_writebackwacache(vma->vm_page_prot);
881 else
882 vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
883
884 /* Remap the frame buffer I/O range */
885 if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT,
886 vma->vm_end - vma->vm_start,
887 vma->vm_page_prot))
888 return -EAGAIN;
889
890 return 0;
891}
892
893static struct fb_ops msm_fb_ops = {
894 .owner = THIS_MODULE,
895 .fb_open = msm_fb_open,
896 .fb_release = msm_fb_release,
897 .fb_read = NULL,
898 .fb_write = NULL,
899 .fb_cursor = NULL,
900 .fb_check_var = msm_fb_check_var, /* vinfo check */
901 .fb_set_par = msm_fb_set_par, /* set the video mode according to info->var */
902 .fb_setcolreg = NULL, /* set color register */
903 .fb_blank = msm_fb_blank, /* blank display */
904 .fb_pan_display = msm_fb_pan_display, /* pan display */
905 .fb_fillrect = msm_fb_fillrect, /* Draws a rectangle */
906 .fb_copyarea = msm_fb_copyarea, /* Copy data from area to another */
907 .fb_imageblit = msm_fb_imageblit, /* Draws a image to the display */
908 .fb_rotate = NULL,
909 .fb_sync = NULL, /* wait for blit idle, optional */
910 .fb_ioctl = msm_fb_ioctl, /* perform fb specific ioctl (optional) */
911 .fb_mmap = msm_fb_mmap,
912};
913
914static __u32 msm_fb_line_length(__u32 fb_index, __u32 xres, int bpp)
915{
916 /* The adreno GPU hardware requires that the pitch be aligned to
917 32 pixels for color buffers, so for the cases where the GPU
918 is writing directly to fb0, the framebuffer pitch
919 also needs to be 32 pixel aligned */
920
921 if (fb_index == 0)
922 return ALIGN(xres, 32) * bpp;
923 else
924 return xres * bpp;
925}
926
927static int msm_fb_register(struct msm_fb_data_type *mfd)
928{
929 int ret = -ENODEV;
930 int bpp;
931 struct msm_panel_info *panel_info = &mfd->panel_info;
932 struct fb_info *fbi = mfd->fbi;
933 struct fb_fix_screeninfo *fix;
934 struct fb_var_screeninfo *var;
935 int *id;
936 int fbram_offset;
937
938 /*
939 * fb info initialization
940 */
941 fix = &fbi->fix;
942 var = &fbi->var;
943
944 fix->type_aux = 0; /* if type == FB_TYPE_INTERLEAVED_PLANES */
945 fix->visual = FB_VISUAL_TRUECOLOR; /* True Color */
946 fix->ywrapstep = 0; /* No support */
947 fix->mmio_start = 0; /* No MMIO Address */
948 fix->mmio_len = 0; /* No MMIO Address */
949 fix->accel = FB_ACCEL_NONE;/* FB_ACCEL_MSM needes to be added in fb.h */
950
951 var->xoffset = 0, /* Offset from virtual to visible */
952 var->yoffset = 0, /* resolution */
953 var->grayscale = 0, /* No graylevels */
954 var->nonstd = 0, /* standard pixel format */
955 var->activate = FB_ACTIVATE_VBL, /* activate it at vsync */
956 var->height = -1, /* height of picture in mm */
957 var->width = -1, /* width of picture in mm */
958 var->accel_flags = 0, /* acceleration flags */
959 var->sync = 0, /* see FB_SYNC_* */
960 var->rotate = 0, /* angle we rotate counter clockwise */
961 mfd->op_enable = FALSE;
962
963 switch (mfd->fb_imgType) {
964 case MDP_RGB_565:
965 fix->type = FB_TYPE_PACKED_PIXELS;
966 fix->xpanstep = 1;
967 fix->ypanstep = 1;
968 var->vmode = FB_VMODE_NONINTERLACED;
969 var->blue.offset = 0;
970 var->green.offset = 5;
971 var->red.offset = 11;
972 var->blue.length = 5;
973 var->green.length = 6;
974 var->red.length = 5;
975 var->blue.msb_right = 0;
976 var->green.msb_right = 0;
977 var->red.msb_right = 0;
978 var->transp.offset = 0;
979 var->transp.length = 0;
980 bpp = 2;
981 break;
982
983 case MDP_RGB_888:
984 fix->type = FB_TYPE_PACKED_PIXELS;
985 fix->xpanstep = 1;
986 fix->ypanstep = 1;
987 var->vmode = FB_VMODE_NONINTERLACED;
988 var->blue.offset = 0;
989 var->green.offset = 8;
990 var->red.offset = 16;
991 var->blue.length = 8;
992 var->green.length = 8;
993 var->red.length = 8;
994 var->blue.msb_right = 0;
995 var->green.msb_right = 0;
996 var->red.msb_right = 0;
997 var->transp.offset = 0;
998 var->transp.length = 0;
999 bpp = 3;
1000 break;
1001
1002 case MDP_ARGB_8888:
1003 fix->type = FB_TYPE_PACKED_PIXELS;
1004 fix->xpanstep = 1;
1005 fix->ypanstep = 1;
1006 var->vmode = FB_VMODE_NONINTERLACED;
1007 var->blue.offset = 0;
1008 var->green.offset = 8;
1009 var->red.offset = 16;
1010 var->blue.length = 8;
1011 var->green.length = 8;
1012 var->red.length = 8;
1013 var->blue.msb_right = 0;
1014 var->green.msb_right = 0;
1015 var->red.msb_right = 0;
1016 var->transp.offset = 24;
1017 var->transp.length = 8;
1018 bpp = 4;
1019 break;
1020
1021 case MDP_RGBA_8888:
1022 fix->type = FB_TYPE_PACKED_PIXELS;
1023 fix->xpanstep = 1;
1024 fix->ypanstep = 1;
1025 var->vmode = FB_VMODE_NONINTERLACED;
1026 var->blue.offset = 8;
1027 var->green.offset = 16;
1028 var->red.offset = 24;
1029 var->blue.length = 8;
1030 var->green.length = 8;
1031 var->red.length = 8;
1032 var->blue.msb_right = 0;
1033 var->green.msb_right = 0;
1034 var->red.msb_right = 0;
1035 var->transp.offset = 0;
1036 var->transp.length = 8;
1037 bpp = 4;
1038 break;
1039
1040 case MDP_YCRYCB_H2V1:
1041 /* ToDo: need to check TV-Out YUV422i framebuffer format */
1042 /* we might need to create new type define */
1043 fix->type = FB_TYPE_INTERLEAVED_PLANES;
1044 fix->xpanstep = 2;
1045 fix->ypanstep = 1;
1046 var->vmode = FB_VMODE_NONINTERLACED;
1047
1048 /* how about R/G/B offset? */
1049 var->blue.offset = 0;
1050 var->green.offset = 5;
1051 var->red.offset = 11;
1052 var->blue.length = 5;
1053 var->green.length = 6;
1054 var->red.length = 5;
1055 var->blue.msb_right = 0;
1056 var->green.msb_right = 0;
1057 var->red.msb_right = 0;
1058 var->transp.offset = 0;
1059 var->transp.length = 0;
1060 bpp = 2;
1061 break;
1062
1063 default:
1064 MSM_FB_ERR("msm_fb_init: fb %d unkown image type!\n",
1065 mfd->index);
1066 return ret;
1067 }
1068
1069 fix->type = panel_info->is_3d_panel;
1070
1071 fix->line_length = msm_fb_line_length(mfd->index, panel_info->xres,
1072 bpp);
1073 /* calculate smem_len based on max size of two supplied modes */
1074 fix->smem_len = roundup(MAX(msm_fb_line_length(mfd->index,
1075 panel_info->xres,
1076 bpp) *
1077 panel_info->yres * mfd->fb_page,
1078 msm_fb_line_length(mfd->index,
1079 panel_info->mode2_xres,
1080 bpp) *
1081 panel_info->mode2_yres * mfd->fb_page), PAGE_SIZE);
1082
1083
1084
1085 mfd->var_xres = panel_info->xres;
1086 mfd->var_yres = panel_info->yres;
1087
1088 var->pixclock = mfd->panel_info.clk_rate;
1089 mfd->var_pixclock = var->pixclock;
1090
1091 var->xres = panel_info->xres;
1092 var->yres = panel_info->yres;
1093 var->xres_virtual = panel_info->xres;
1094 var->yres_virtual = panel_info->yres * mfd->fb_page;
1095 var->bits_per_pixel = bpp * 8; /* FrameBuffer color depth */
1096 if (mfd->dest == DISPLAY_LCD) {
1097 var->reserved[4] = panel_info->lcd.refx100 / 100;
1098 } else {
Ravishangar Kalyanam7a7e46f2011-09-12 12:45:29 -07001099 if (panel_info->type == MIPI_VIDEO_PANEL) {
1100 var->reserved[4] = panel_info->mipi.frame_rate;
1101 } else {
1102 var->reserved[4] = panel_info->clk_rate /
1103 ((panel_info->lcdc.h_back_porch +
1104 panel_info->lcdc.h_front_porch +
1105 panel_info->lcdc.h_pulse_width +
1106 panel_info->xres) *
1107 (panel_info->lcdc.v_back_porch +
1108 panel_info->lcdc.v_front_porch +
1109 panel_info->lcdc.v_pulse_width +
1110 panel_info->yres));
1111 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001112 }
Ravishangar Kalyanam7a7e46f2011-09-12 12:45:29 -07001113 pr_debug("reserved[4] %u\n", var->reserved[4]);
1114
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001115 /*
1116 * id field for fb app
1117 */
1118 id = (int *)&mfd->panel;
1119
1120#if defined(CONFIG_FB_MSM_MDP22)
1121 snprintf(fix->id, sizeof(fix->id), "msmfb22_%x", (__u32) *id);
1122#elif defined(CONFIG_FB_MSM_MDP30)
1123 snprintf(fix->id, sizeof(fix->id), "msmfb30_%x", (__u32) *id);
1124#elif defined(CONFIG_FB_MSM_MDP31)
1125 snprintf(fix->id, sizeof(fix->id), "msmfb31_%x", (__u32) *id);
1126#elif defined(CONFIG_FB_MSM_MDP40)
1127 snprintf(fix->id, sizeof(fix->id), "msmfb40_%x", (__u32) *id);
1128#else
1129 error CONFIG_FB_MSM_MDP undefined !
1130#endif
1131 fbi->fbops = &msm_fb_ops;
1132 fbi->flags = FBINFO_FLAG_DEFAULT;
1133 fbi->pseudo_palette = msm_fb_pseudo_palette;
1134
1135 mfd->ref_cnt = 0;
1136 mfd->sw_currently_refreshing = FALSE;
1137 mfd->sw_refreshing_enable = TRUE;
1138 mfd->panel_power_on = FALSE;
1139
1140 mfd->pan_waiting = FALSE;
1141 init_completion(&mfd->pan_comp);
1142 init_completion(&mfd->refresher_comp);
1143 sema_init(&mfd->sem, 1);
1144
1145 init_timer(&mfd->msmfb_no_update_notify_timer);
1146 mfd->msmfb_no_update_notify_timer.function =
1147 msmfb_no_update_notify_timer_cb;
1148 mfd->msmfb_no_update_notify_timer.data = (unsigned long)mfd;
1149 init_completion(&mfd->msmfb_update_notify);
1150 init_completion(&mfd->msmfb_no_update_notify);
1151
1152 fbram_offset = PAGE_ALIGN((int)fbram)-(int)fbram;
1153 fbram += fbram_offset;
1154 fbram_phys += fbram_offset;
1155 fbram_size -= fbram_offset;
1156
1157 if (fbram_size < fix->smem_len) {
1158 printk(KERN_ERR "error: no more framebuffer memory!\n");
1159 return -ENOMEM;
1160 }
1161
1162 fbi->screen_base = fbram;
1163 fbi->fix.smem_start = (unsigned long)fbram_phys;
1164
1165 memset(fbi->screen_base, 0x0, fix->smem_len);
1166
1167 mfd->op_enable = TRUE;
1168 mfd->panel_power_on = FALSE;
1169
1170 /* cursor memory allocation */
1171 if (mfd->cursor_update) {
1172 mfd->cursor_buf = dma_alloc_coherent(NULL,
1173 MDP_CURSOR_SIZE,
1174 (dma_addr_t *) &mfd->cursor_buf_phys,
1175 GFP_KERNEL);
1176 if (!mfd->cursor_buf)
1177 mfd->cursor_update = 0;
1178 }
1179
1180 if (mfd->lut_update) {
1181 ret = fb_alloc_cmap(&fbi->cmap, 256, 0);
1182 if (ret)
1183 printk(KERN_ERR "%s: fb_alloc_cmap() failed!\n",
1184 __func__);
1185 }
1186
1187 if (register_framebuffer(fbi) < 0) {
1188 if (mfd->lut_update)
1189 fb_dealloc_cmap(&fbi->cmap);
1190
1191 if (mfd->cursor_buf)
1192 dma_free_coherent(NULL,
1193 MDP_CURSOR_SIZE,
1194 mfd->cursor_buf,
1195 (dma_addr_t) mfd->cursor_buf_phys);
1196
1197 mfd->op_enable = FALSE;
1198 return -EPERM;
1199 }
1200
1201 fbram += fix->smem_len;
1202 fbram_phys += fix->smem_len;
1203 fbram_size -= fix->smem_len;
1204
1205 MSM_FB_INFO
1206 ("FrameBuffer[%d] %dx%d size=%d bytes is registered successfully!\n",
1207 mfd->index, fbi->var.xres, fbi->var.yres, fbi->fix.smem_len);
1208
1209#ifdef CONFIG_FB_MSM_LOGO
1210 if (!load_565rle_image(INIT_IMAGE_FILE)) ; /* Flip buffer */
1211#endif
1212 ret = 0;
1213
1214#ifdef CONFIG_HAS_EARLYSUSPEND
1215 if (mfd->panel_info.type != DTV_PANEL) {
1216 mfd->early_suspend.suspend = msmfb_early_suspend;
1217 mfd->early_suspend.resume = msmfb_early_resume;
1218 mfd->early_suspend.level = EARLY_SUSPEND_LEVEL_DISABLE_FB - 2;
1219 register_early_suspend(&mfd->early_suspend);
1220 }
1221#endif
1222
1223#ifdef MSM_FB_ENABLE_DBGFS
1224 {
1225 struct dentry *root;
1226 struct dentry *sub_dir;
1227 char sub_name[2];
1228
1229 root = msm_fb_get_debugfs_root();
1230 if (root != NULL) {
1231 sub_name[0] = (char)(mfd->index + 0x30);
1232 sub_name[1] = '\0';
1233 sub_dir = debugfs_create_dir(sub_name, root);
1234 } else {
1235 sub_dir = NULL;
1236 }
1237
1238 mfd->sub_dir = sub_dir;
1239
1240 if (sub_dir) {
1241 msm_fb_debugfs_file_create(sub_dir, "op_enable",
1242 (u32 *) &mfd->op_enable);
1243 msm_fb_debugfs_file_create(sub_dir, "panel_power_on",
1244 (u32 *) &mfd->
1245 panel_power_on);
1246 msm_fb_debugfs_file_create(sub_dir, "ref_cnt",
1247 (u32 *) &mfd->ref_cnt);
1248 msm_fb_debugfs_file_create(sub_dir, "fb_imgType",
1249 (u32 *) &mfd->fb_imgType);
1250 msm_fb_debugfs_file_create(sub_dir,
1251 "sw_currently_refreshing",
1252 (u32 *) &mfd->
1253 sw_currently_refreshing);
1254 msm_fb_debugfs_file_create(sub_dir,
1255 "sw_refreshing_enable",
1256 (u32 *) &mfd->
1257 sw_refreshing_enable);
1258
1259 msm_fb_debugfs_file_create(sub_dir, "xres",
1260 (u32 *) &mfd->panel_info.
1261 xres);
1262 msm_fb_debugfs_file_create(sub_dir, "yres",
1263 (u32 *) &mfd->panel_info.
1264 yres);
1265 msm_fb_debugfs_file_create(sub_dir, "bpp",
1266 (u32 *) &mfd->panel_info.
1267 bpp);
1268 msm_fb_debugfs_file_create(sub_dir, "type",
1269 (u32 *) &mfd->panel_info.
1270 type);
1271 msm_fb_debugfs_file_create(sub_dir, "wait_cycle",
1272 (u32 *) &mfd->panel_info.
1273 wait_cycle);
1274 msm_fb_debugfs_file_create(sub_dir, "pdest",
1275 (u32 *) &mfd->panel_info.
1276 pdest);
1277 msm_fb_debugfs_file_create(sub_dir, "backbuff",
1278 (u32 *) &mfd->panel_info.
1279 fb_num);
1280 msm_fb_debugfs_file_create(sub_dir, "clk_rate",
1281 (u32 *) &mfd->panel_info.
1282 clk_rate);
1283 msm_fb_debugfs_file_create(sub_dir, "frame_count",
1284 (u32 *) &mfd->panel_info.
1285 frame_count);
1286
1287
1288 switch (mfd->dest) {
1289 case DISPLAY_LCD:
1290 msm_fb_debugfs_file_create(sub_dir,
1291 "vsync_enable",
1292 (u32 *)&mfd->panel_info.lcd.vsync_enable);
1293 msm_fb_debugfs_file_create(sub_dir,
1294 "refx100",
1295 (u32 *) &mfd->panel_info.lcd. refx100);
1296 msm_fb_debugfs_file_create(sub_dir,
1297 "v_back_porch",
1298 (u32 *) &mfd->panel_info.lcd.v_back_porch);
1299 msm_fb_debugfs_file_create(sub_dir,
1300 "v_front_porch",
1301 (u32 *) &mfd->panel_info.lcd.v_front_porch);
1302 msm_fb_debugfs_file_create(sub_dir,
1303 "v_pulse_width",
1304 (u32 *) &mfd->panel_info.lcd.v_pulse_width);
1305 msm_fb_debugfs_file_create(sub_dir,
1306 "hw_vsync_mode",
1307 (u32 *) &mfd->panel_info.lcd.hw_vsync_mode);
1308 msm_fb_debugfs_file_create(sub_dir,
1309 "vsync_notifier_period", (u32 *)
1310 &mfd->panel_info.lcd.vsync_notifier_period);
1311 break;
1312
1313 case DISPLAY_LCDC:
1314 msm_fb_debugfs_file_create(sub_dir,
1315 "h_back_porch",
1316 (u32 *) &mfd->panel_info.lcdc.h_back_porch);
1317 msm_fb_debugfs_file_create(sub_dir,
1318 "h_front_porch",
1319 (u32 *) &mfd->panel_info.lcdc.h_front_porch);
1320 msm_fb_debugfs_file_create(sub_dir,
1321 "h_pulse_width",
1322 (u32 *) &mfd->panel_info.lcdc.h_pulse_width);
1323 msm_fb_debugfs_file_create(sub_dir,
1324 "v_back_porch",
1325 (u32 *) &mfd->panel_info.lcdc.v_back_porch);
1326 msm_fb_debugfs_file_create(sub_dir,
1327 "v_front_porch",
1328 (u32 *) &mfd->panel_info.lcdc.v_front_porch);
1329 msm_fb_debugfs_file_create(sub_dir,
1330 "v_pulse_width",
1331 (u32 *) &mfd->panel_info.lcdc.v_pulse_width);
1332 msm_fb_debugfs_file_create(sub_dir,
1333 "border_clr",
1334 (u32 *) &mfd->panel_info.lcdc.border_clr);
1335 msm_fb_debugfs_file_create(sub_dir,
1336 "underflow_clr",
1337 (u32 *) &mfd->panel_info.lcdc.underflow_clr);
1338 msm_fb_debugfs_file_create(sub_dir,
1339 "hsync_skew",
1340 (u32 *) &mfd->panel_info.lcdc.hsync_skew);
1341 break;
1342
1343 default:
1344 break;
1345 }
1346 }
1347 }
1348#endif /* MSM_FB_ENABLE_DBGFS */
1349
1350 return ret;
1351}
1352
1353static int msm_fb_open(struct fb_info *info, int user)
1354{
1355 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
1356 int result;
1357
1358 result = pm_runtime_get_sync(info->dev);
1359
1360 if (result < 0) {
1361 printk(KERN_ERR "pm_runtime: fail to wake up\n");
1362 }
1363
1364
1365 if (!mfd->ref_cnt) {
1366 mdp_set_dma_pan_info(info, NULL, TRUE);
1367
1368 if (msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable)) {
1369 printk(KERN_ERR "msm_fb_open: can't turn on display!\n");
1370 return -1;
1371 }
1372 }
1373
1374 mfd->ref_cnt++;
1375 return 0;
1376}
1377
1378static int msm_fb_release(struct fb_info *info, int user)
1379{
1380 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
1381 int ret = 0;
1382
1383 if (!mfd->ref_cnt) {
1384 MSM_FB_INFO("msm_fb_release: try to close unopened fb %d!\n",
1385 mfd->index);
1386 return -EINVAL;
1387 }
1388
1389 mfd->ref_cnt--;
1390
1391 if (!mfd->ref_cnt) {
1392 if ((ret =
1393 msm_fb_blank_sub(FB_BLANK_POWERDOWN, info,
1394 mfd->op_enable)) != 0) {
1395 printk(KERN_ERR "msm_fb_release: can't turn off display!\n");
1396 return ret;
1397 }
1398 }
1399
1400 pm_runtime_put(info->dev);
1401 return ret;
1402}
1403
1404DEFINE_SEMAPHORE(msm_fb_pan_sem);
1405
1406static int msm_fb_pan_display(struct fb_var_screeninfo *var,
1407 struct fb_info *info)
1408{
1409 struct mdp_dirty_region dirty;
1410 struct mdp_dirty_region *dirtyPtr = NULL;
1411 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
Ravishangar Kalyanam6a75b862011-12-15 10:33:39 -08001412 struct msm_fb_panel_data *pdata;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001413
1414 if ((!mfd->op_enable) || (!mfd->panel_power_on))
1415 return -EPERM;
1416
1417 if (var->xoffset > (info->var.xres_virtual - info->var.xres))
1418 return -EINVAL;
1419
1420 if (var->yoffset > (info->var.yres_virtual - info->var.yres))
1421 return -EINVAL;
1422
1423 if (info->fix.xpanstep)
1424 info->var.xoffset =
1425 (var->xoffset / info->fix.xpanstep) * info->fix.xpanstep;
1426
1427 if (info->fix.ypanstep)
1428 info->var.yoffset =
1429 (var->yoffset / info->fix.ypanstep) * info->fix.ypanstep;
1430
1431 /* "UPDT" */
1432 if (var->reserved[0] == 0x54445055) {
1433 dirty.xoffset = var->reserved[1] & 0xffff;
1434 dirty.yoffset = (var->reserved[1] >> 16) & 0xffff;
1435
1436 if ((var->reserved[2] & 0xffff) <= dirty.xoffset)
1437 return -EINVAL;
1438 if (((var->reserved[2] >> 16) & 0xffff) <= dirty.yoffset)
1439 return -EINVAL;
1440
1441 dirty.width = (var->reserved[2] & 0xffff) - dirty.xoffset;
1442 dirty.height =
1443 ((var->reserved[2] >> 16) & 0xffff) - dirty.yoffset;
1444 info->var.yoffset = var->yoffset;
1445
1446 if (dirty.xoffset < 0)
1447 return -EINVAL;
1448
1449 if (dirty.yoffset < 0)
1450 return -EINVAL;
1451
1452 if ((dirty.xoffset + dirty.width) > info->var.xres)
1453 return -EINVAL;
1454
1455 if ((dirty.yoffset + dirty.height) > info->var.yres)
1456 return -EINVAL;
1457
1458 if ((dirty.width <= 0) || (dirty.height <= 0))
1459 return -EINVAL;
1460
1461 dirtyPtr = &dirty;
1462 }
1463 complete(&mfd->msmfb_update_notify);
1464 mutex_lock(&msm_fb_notify_update_sem);
1465 if (mfd->msmfb_no_update_notify_timer.function)
1466 del_timer(&mfd->msmfb_no_update_notify_timer);
1467
1468 mfd->msmfb_no_update_notify_timer.expires =
1469 jiffies + ((1000 * HZ) / 1000);
1470 add_timer(&mfd->msmfb_no_update_notify_timer);
1471 mutex_unlock(&msm_fb_notify_update_sem);
1472
1473 down(&msm_fb_pan_sem);
1474 mdp_set_dma_pan_info(info, dirtyPtr,
1475 (var->activate == FB_ACTIVATE_VBL));
1476 mdp_dma_pan_update(info);
1477 up(&msm_fb_pan_sem);
1478
Ravishangar Kalyanam6a75b862011-12-15 10:33:39 -08001479 if (unset_bl_level && !bl_updated) {
1480 pdata = (struct msm_fb_panel_data *)mfd->pdev->
1481 dev.platform_data;
1482 if ((pdata) && (pdata->set_backlight)) {
1483 down(&mfd->sem);
1484 mfd->bl_level = unset_bl_level;
1485 pdata->set_backlight(mfd);
1486 bl_level_old = unset_bl_level;
1487 up(&mfd->sem);
1488 }
1489 bl_updated = 1;
1490 }
1491
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07001492 ++mfd->panel_info.frame_count;
1493 return 0;
1494}
1495
1496static int msm_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info)
1497{
1498 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
1499
1500 if (var->rotate != FB_ROTATE_UR)
1501 return -EINVAL;
1502 if (var->grayscale != info->var.grayscale)
1503 return -EINVAL;
1504
1505 switch (var->bits_per_pixel) {
1506 case 16:
1507 if ((var->green.offset != 5) ||
1508 !((var->blue.offset == 11)
1509 || (var->blue.offset == 0)) ||
1510 !((var->red.offset == 11)
1511 || (var->red.offset == 0)) ||
1512 (var->blue.length != 5) ||
1513 (var->green.length != 6) ||
1514 (var->red.length != 5) ||
1515 (var->blue.msb_right != 0) ||
1516 (var->green.msb_right != 0) ||
1517 (var->red.msb_right != 0) ||
1518 (var->transp.offset != 0) ||
1519 (var->transp.length != 0))
1520 return -EINVAL;
1521 break;
1522
1523 case 24:
1524 if ((var->blue.offset != 0) ||
1525 (var->green.offset != 8) ||
1526 (var->red.offset != 16) ||
1527 (var->blue.length != 8) ||
1528 (var->green.length != 8) ||
1529 (var->red.length != 8) ||
1530 (var->blue.msb_right != 0) ||
1531 (var->green.msb_right != 0) ||
1532 (var->red.msb_right != 0) ||
1533 !(((var->transp.offset == 0) &&
1534 (var->transp.length == 0)) ||
1535 ((var->transp.offset == 24) &&
1536 (var->transp.length == 8))))
1537 return -EINVAL;
1538 break;
1539
1540 case 32:
1541 /* Figure out if the user meant RGBA or ARGB
1542 and verify the position of the RGB components */
1543
1544 if (var->transp.offset == 24) {
1545 if ((var->blue.offset != 0) ||
1546 (var->green.offset != 8) ||
1547 (var->red.offset != 16))
1548 return -EINVAL;
1549 } else if (var->transp.offset == 0) {
1550 if ((var->blue.offset != 8) ||
1551 (var->green.offset != 16) ||
1552 (var->red.offset != 24))
1553 return -EINVAL;
1554 } else
1555 return -EINVAL;
1556
1557 /* Check the common values for both RGBA and ARGB */
1558
1559 if ((var->blue.length != 8) ||
1560 (var->green.length != 8) ||
1561 (var->red.length != 8) ||
1562 (var->transp.length != 8) ||
1563 (var->blue.msb_right != 0) ||
1564 (var->green.msb_right != 0) ||
1565 (var->red.msb_right != 0))
1566 return -EINVAL;
1567
1568 break;
1569
1570 default:
1571 return -EINVAL;
1572 }
1573
1574 if ((var->xres_virtual <= 0) || (var->yres_virtual <= 0))
1575 return -EINVAL;
1576
1577 if (info->fix.smem_len <
1578 (var->xres_virtual*var->yres_virtual*(var->bits_per_pixel/8)))
1579 return -EINVAL;
1580
1581 if ((var->xres == 0) || (var->yres == 0))
1582 return -EINVAL;
1583
1584 if ((var->xres > MAX(mfd->panel_info.xres,
1585 mfd->panel_info.mode2_xres)) ||
1586 (var->yres > MAX(mfd->panel_info.yres,
1587 mfd->panel_info.mode2_yres)))
1588 return -EINVAL;
1589
1590 if (var->xoffset > (var->xres_virtual - var->xres))
1591 return -EINVAL;
1592
1593 if (var->yoffset > (var->yres_virtual - var->yres))
1594 return -EINVAL;
1595
1596 return 0;
1597}
1598
1599static int msm_fb_set_par(struct fb_info *info)
1600{
1601 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
1602 struct fb_var_screeninfo *var = &info->var;
1603 int old_imgType;
1604 int blank = 0;
1605
1606 old_imgType = mfd->fb_imgType;
1607 switch (var->bits_per_pixel) {
1608 case 16:
1609 if (var->red.offset == 0)
1610 mfd->fb_imgType = MDP_BGR_565;
1611 else
1612 mfd->fb_imgType = MDP_RGB_565;
1613 break;
1614
1615 case 24:
1616 if ((var->transp.offset == 0) && (var->transp.length == 0))
1617 mfd->fb_imgType = MDP_RGB_888;
1618 else if ((var->transp.offset == 24) &&
1619 (var->transp.length == 8)) {
1620 mfd->fb_imgType = MDP_ARGB_8888;
1621 info->var.bits_per_pixel = 32;
1622 }
1623 break;
1624
1625 case 32:
1626 if (var->transp.offset == 24)
1627 mfd->fb_imgType = MDP_ARGB_8888;
1628 else
1629 mfd->fb_imgType = MDP_RGBA_8888;
1630 break;
1631
1632 default:
1633 return -EINVAL;
1634 }
1635
1636 if ((mfd->var_pixclock != var->pixclock) ||
1637 (mfd->hw_refresh && ((mfd->fb_imgType != old_imgType) ||
1638 (mfd->var_pixclock != var->pixclock) ||
1639 (mfd->var_xres != var->xres) ||
1640 (mfd->var_yres != var->yres)))) {
1641 mfd->var_xres = var->xres;
1642 mfd->var_yres = var->yres;
1643 mfd->var_pixclock = var->pixclock;
1644 blank = 1;
1645 }
1646 mfd->fbi->fix.line_length = msm_fb_line_length(mfd->index, var->xres,
1647 var->bits_per_pixel/8);
1648
1649 if (blank) {
1650 msm_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable);
1651 msm_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable);
1652 }
1653
1654 return 0;
1655}
1656
1657static int msm_fb_stop_sw_refresher(struct msm_fb_data_type *mfd)
1658{
1659 if (mfd->hw_refresh)
1660 return -EPERM;
1661
1662 if (mfd->sw_currently_refreshing) {
1663 down(&mfd->sem);
1664 mfd->sw_currently_refreshing = FALSE;
1665 up(&mfd->sem);
1666
1667 /* wait until the refresher finishes the last job */
1668 wait_for_completion_killable(&mfd->refresher_comp);
1669 }
1670
1671 return 0;
1672}
1673
1674int msm_fb_resume_sw_refresher(struct msm_fb_data_type *mfd)
1675{
1676 boolean do_refresh;
1677
1678 if (mfd->hw_refresh)
1679 return -EPERM;
1680
1681 down(&mfd->sem);
1682 if ((!mfd->sw_currently_refreshing) && (mfd->sw_refreshing_enable)) {
1683 do_refresh = TRUE;
1684 mfd->sw_currently_refreshing = TRUE;
1685 } else {
1686 do_refresh = FALSE;
1687 }
1688 up(&mfd->sem);
1689
1690 if (do_refresh)
1691 mdp_refresh_screen((unsigned long)mfd);
1692
1693 return 0;
1694}
1695
1696#if defined CONFIG_FB_MSM_MDP31
1697static int mdp_blit_split_height(struct fb_info *info,
1698 struct mdp_blit_req *req)
1699{
1700 int ret;
1701 struct mdp_blit_req splitreq;
1702 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;
1703 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;
1704
1705 splitreq = *req;
1706 /* break dest roi at height*/
1707 d_x_0 = d_x_1 = req->dst_rect.x;
1708 d_w_0 = d_w_1 = req->dst_rect.w;
1709 d_y_0 = req->dst_rect.y;
1710 if (req->dst_rect.h % 32 == 3)
1711 d_h_1 = (req->dst_rect.h - 3) / 2 - 1;
1712 else if (req->dst_rect.h % 32 == 2)
1713 d_h_1 = (req->dst_rect.h - 2) / 2 - 6;
1714 else
1715 d_h_1 = (req->dst_rect.h - 1) / 2 - 1;
1716 d_h_0 = req->dst_rect.h - d_h_1;
1717 d_y_1 = d_y_0 + d_h_0;
1718 if (req->dst_rect.h == 3) {
1719 d_h_1 = 2;
1720 d_h_0 = 2;
1721 d_y_1 = d_y_0 + 1;
1722 }
1723
1724 /* blit first region */
1725 if (((splitreq.flags & 0x07) == 0x04) ||
1726 ((splitreq.flags & 0x07) == 0x0)) {
1727
1728 if (splitreq.flags & MDP_ROT_90) {
1729 s_y_0 = s_y_1 = req->src_rect.y;
1730 s_h_0 = s_h_1 = req->src_rect.h;
1731 s_x_0 = req->src_rect.x;
1732 s_w_1 = (req->src_rect.w * d_h_1) / req->dst_rect.h;
1733 s_w_0 = req->src_rect.w - s_w_1;
1734 s_x_1 = s_x_0 + s_w_0;
1735 if (d_h_1 >= 8 * s_w_1) {
1736 s_w_1++;
1737 s_x_1--;
1738 }
1739 } else {
1740 s_x_0 = s_x_1 = req->src_rect.x;
1741 s_w_0 = s_w_1 = req->src_rect.w;
1742 s_y_0 = req->src_rect.y;
1743 s_h_1 = (req->src_rect.h * d_h_1) / req->dst_rect.h;
1744 s_h_0 = req->src_rect.h - s_h_1;
1745 s_y_1 = s_y_0 + s_h_0;
1746 if (d_h_1 >= 8 * s_h_1) {
1747 s_h_1++;
1748 s_y_1--;
1749 }
1750 }
1751
1752 splitreq.src_rect.h = s_h_0;
1753 splitreq.src_rect.y = s_y_0;
1754 splitreq.dst_rect.h = d_h_0;
1755 splitreq.dst_rect.y = d_y_0;
1756 splitreq.src_rect.x = s_x_0;
1757 splitreq.src_rect.w = s_w_0;
1758 splitreq.dst_rect.x = d_x_0;
1759 splitreq.dst_rect.w = d_w_0;
1760 } else {
1761
1762 if (splitreq.flags & MDP_ROT_90) {
1763 s_y_0 = s_y_1 = req->src_rect.y;
1764 s_h_0 = s_h_1 = req->src_rect.h;
1765 s_x_0 = req->src_rect.x;
1766 s_w_1 = (req->src_rect.w * d_h_0) / req->dst_rect.h;
1767 s_w_0 = req->src_rect.w - s_w_1;
1768 s_x_1 = s_x_0 + s_w_0;
1769 if (d_h_0 >= 8 * s_w_1) {
1770 s_w_1++;
1771 s_x_1--;
1772 }
1773 } else {
1774 s_x_0 = s_x_1 = req->src_rect.x;
1775 s_w_0 = s_w_1 = req->src_rect.w;
1776 s_y_0 = req->src_rect.y;
1777 s_h_1 = (req->src_rect.h * d_h_0) / req->dst_rect.h;
1778 s_h_0 = req->src_rect.h - s_h_1;
1779 s_y_1 = s_y_0 + s_h_0;
1780 if (d_h_0 >= 8 * s_h_1) {
1781 s_h_1++;
1782 s_y_1--;
1783 }
1784 }
1785 splitreq.src_rect.h = s_h_0;
1786 splitreq.src_rect.y = s_y_0;
1787 splitreq.dst_rect.h = d_h_1;
1788 splitreq.dst_rect.y = d_y_1;
1789 splitreq.src_rect.x = s_x_0;
1790 splitreq.src_rect.w = s_w_0;
1791 splitreq.dst_rect.x = d_x_1;
1792 splitreq.dst_rect.w = d_w_1;
1793 }
1794 ret = mdp_ppp_blit(info, &splitreq);
1795 if (ret)
1796 return ret;
1797
1798 /* blit second region */
1799 if (((splitreq.flags & 0x07) == 0x04) ||
1800 ((splitreq.flags & 0x07) == 0x0)) {
1801 splitreq.src_rect.h = s_h_1;
1802 splitreq.src_rect.y = s_y_1;
1803 splitreq.dst_rect.h = d_h_1;
1804 splitreq.dst_rect.y = d_y_1;
1805 splitreq.src_rect.x = s_x_1;
1806 splitreq.src_rect.w = s_w_1;
1807 splitreq.dst_rect.x = d_x_1;
1808 splitreq.dst_rect.w = d_w_1;
1809 } else {
1810 splitreq.src_rect.h = s_h_1;
1811 splitreq.src_rect.y = s_y_1;
1812 splitreq.dst_rect.h = d_h_0;
1813 splitreq.dst_rect.y = d_y_0;
1814 splitreq.src_rect.x = s_x_1;
1815 splitreq.src_rect.w = s_w_1;
1816 splitreq.dst_rect.x = d_x_0;
1817 splitreq.dst_rect.w = d_w_0;
1818 }
1819 ret = mdp_ppp_blit(info, &splitreq);
1820 return ret;
1821}
1822#endif
1823
1824int mdp_blit(struct fb_info *info, struct mdp_blit_req *req)
1825{
1826 int ret;
1827#if defined CONFIG_FB_MSM_MDP31 || defined CONFIG_FB_MSM_MDP30
1828 unsigned int remainder = 0, is_bpp_4 = 0;
1829 struct mdp_blit_req splitreq;
1830 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;
1831 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;
1832
1833 if (req->flags & MDP_ROT_90) {
1834 if (((req->dst_rect.h == 1) && ((req->src_rect.w != 1) ||
1835 (req->dst_rect.w != req->src_rect.h))) ||
1836 ((req->dst_rect.w == 1) && ((req->src_rect.h != 1) ||
1837 (req->dst_rect.h != req->src_rect.w)))) {
1838 printk(KERN_ERR "mpd_ppp: error scaling when size is 1!\n");
1839 return -EINVAL;
1840 }
1841 } else {
1842 if (((req->dst_rect.w == 1) && ((req->src_rect.w != 1) ||
1843 (req->dst_rect.h != req->src_rect.h))) ||
1844 ((req->dst_rect.h == 1) && ((req->src_rect.h != 1) ||
1845 (req->dst_rect.w != req->src_rect.w)))) {
1846 printk(KERN_ERR "mpd_ppp: error scaling when size is 1!\n");
1847 return -EINVAL;
1848 }
1849 }
1850#endif
1851 if (unlikely(req->src_rect.h == 0 || req->src_rect.w == 0)) {
1852 printk(KERN_ERR "mpd_ppp: src img of zero size!\n");
1853 return -EINVAL;
1854 }
1855 if (unlikely(req->dst_rect.h == 0 || req->dst_rect.w == 0))
1856 return 0;
1857
1858#if defined CONFIG_FB_MSM_MDP31
1859 /* MDP width split workaround */
1860 remainder = (req->dst_rect.w)%32;
1861 ret = mdp_get_bytes_per_pixel(req->dst.format,
1862 (struct msm_fb_data_type *)info->par);
1863 if (ret <= 0) {
1864 printk(KERN_ERR "mdp_ppp: incorrect bpp!\n");
1865 return -EINVAL;
1866 }
1867 is_bpp_4 = (ret == 4) ? 1 : 0;
1868
1869 if ((is_bpp_4 && (remainder == 6 || remainder == 14 ||
1870 remainder == 22 || remainder == 30)) || remainder == 3 ||
1871 (remainder == 1 && req->dst_rect.w != 1) ||
1872 (remainder == 2 && req->dst_rect.w != 2)) {
1873 /* make new request as provide by user */
1874 splitreq = *req;
1875
1876 /* break dest roi at width*/
1877 d_y_0 = d_y_1 = req->dst_rect.y;
1878 d_h_0 = d_h_1 = req->dst_rect.h;
1879 d_x_0 = req->dst_rect.x;
1880
1881 if (remainder == 14)
1882 d_w_1 = (req->dst_rect.w - 14) / 2 + 4;
1883 else if (remainder == 22)
1884 d_w_1 = (req->dst_rect.w - 22) / 2 + 10;
1885 else if (remainder == 30)
1886 d_w_1 = (req->dst_rect.w - 30) / 2 + 10;
1887 else if (remainder == 6)
1888 d_w_1 = req->dst_rect.w / 2 - 1;
1889 else if (remainder == 3)
1890 d_w_1 = (req->dst_rect.w - 3) / 2 - 1;
1891 else if (remainder == 2)
1892 d_w_1 = (req->dst_rect.w - 2) / 2 - 6;
1893 else
1894 d_w_1 = (req->dst_rect.w - 1) / 2 - 1;
1895 d_w_0 = req->dst_rect.w - d_w_1;
1896 d_x_1 = d_x_0 + d_w_0;
1897 if (req->dst_rect.w == 3) {
1898 d_w_1 = 2;
1899 d_w_0 = 2;
1900 d_x_1 = d_x_0 + 1;
1901 }
1902
1903 /* blit first region */
1904 if (((splitreq.flags & 0x07) == 0x07) ||
1905 ((splitreq.flags & 0x07) == 0x0)) {
1906
1907 if (splitreq.flags & MDP_ROT_90) {
1908 s_x_0 = s_x_1 = req->src_rect.x;
1909 s_w_0 = s_w_1 = req->src_rect.w;
1910 s_y_0 = req->src_rect.y;
1911 s_h_1 = (req->src_rect.h * d_w_1) /
1912 req->dst_rect.w;
1913 s_h_0 = req->src_rect.h - s_h_1;
1914 s_y_1 = s_y_0 + s_h_0;
1915 if (d_w_1 >= 8 * s_h_1) {
1916 s_h_1++;
1917 s_y_1--;
1918 }
1919 } else {
1920 s_y_0 = s_y_1 = req->src_rect.y;
1921 s_h_0 = s_h_1 = req->src_rect.h;
1922 s_x_0 = req->src_rect.x;
1923 s_w_1 = (req->src_rect.w * d_w_1) /
1924 req->dst_rect.w;
1925 s_w_0 = req->src_rect.w - s_w_1;
1926 s_x_1 = s_x_0 + s_w_0;
1927 if (d_w_1 >= 8 * s_w_1) {
1928 s_w_1++;
1929 s_x_1--;
1930 }
1931 }
1932
1933 splitreq.src_rect.h = s_h_0;
1934 splitreq.src_rect.y = s_y_0;
1935 splitreq.dst_rect.h = d_h_0;
1936 splitreq.dst_rect.y = d_y_0;
1937 splitreq.src_rect.x = s_x_0;
1938 splitreq.src_rect.w = s_w_0;
1939 splitreq.dst_rect.x = d_x_0;
1940 splitreq.dst_rect.w = d_w_0;
1941 } else {
1942 if (splitreq.flags & MDP_ROT_90) {
1943 s_x_0 = s_x_1 = req->src_rect.x;
1944 s_w_0 = s_w_1 = req->src_rect.w;
1945 s_y_0 = req->src_rect.y;
1946 s_h_1 = (req->src_rect.h * d_w_0) /
1947 req->dst_rect.w;
1948 s_h_0 = req->src_rect.h - s_h_1;
1949 s_y_1 = s_y_0 + s_h_0;
1950 if (d_w_0 >= 8 * s_h_1) {
1951 s_h_1++;
1952 s_y_1--;
1953 }
1954 } else {
1955 s_y_0 = s_y_1 = req->src_rect.y;
1956 s_h_0 = s_h_1 = req->src_rect.h;
1957 s_x_0 = req->src_rect.x;
1958 s_w_1 = (req->src_rect.w * d_w_0) /
1959 req->dst_rect.w;
1960 s_w_0 = req->src_rect.w - s_w_1;
1961 s_x_1 = s_x_0 + s_w_0;
1962 if (d_w_0 >= 8 * s_w_1) {
1963 s_w_1++;
1964 s_x_1--;
1965 }
1966 }
1967 splitreq.src_rect.h = s_h_0;
1968 splitreq.src_rect.y = s_y_0;
1969 splitreq.dst_rect.h = d_h_1;
1970 splitreq.dst_rect.y = d_y_1;
1971 splitreq.src_rect.x = s_x_0;
1972 splitreq.src_rect.w = s_w_0;
1973 splitreq.dst_rect.x = d_x_1;
1974 splitreq.dst_rect.w = d_w_1;
1975 }
1976
1977 if ((splitreq.dst_rect.h % 32 == 3) ||
1978 ((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) ||
1979 ((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2))
1980 ret = mdp_blit_split_height(info, &splitreq);
1981 else
1982 ret = mdp_ppp_blit(info, &splitreq);
1983 if (ret)
1984 return ret;
1985 /* blit second region */
1986 if (((splitreq.flags & 0x07) == 0x07) ||
1987 ((splitreq.flags & 0x07) == 0x0)) {
1988 splitreq.src_rect.h = s_h_1;
1989 splitreq.src_rect.y = s_y_1;
1990 splitreq.dst_rect.h = d_h_1;
1991 splitreq.dst_rect.y = d_y_1;
1992 splitreq.src_rect.x = s_x_1;
1993 splitreq.src_rect.w = s_w_1;
1994 splitreq.dst_rect.x = d_x_1;
1995 splitreq.dst_rect.w = d_w_1;
1996 } else {
1997 splitreq.src_rect.h = s_h_1;
1998 splitreq.src_rect.y = s_y_1;
1999 splitreq.dst_rect.h = d_h_0;
2000 splitreq.dst_rect.y = d_y_0;
2001 splitreq.src_rect.x = s_x_1;
2002 splitreq.src_rect.w = s_w_1;
2003 splitreq.dst_rect.x = d_x_0;
2004 splitreq.dst_rect.w = d_w_0;
2005 }
2006 if (((splitreq.dst_rect.h % 32) == 3) ||
2007 ((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) ||
2008 ((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2))
2009 ret = mdp_blit_split_height(info, &splitreq);
2010 else
2011 ret = mdp_ppp_blit(info, &splitreq);
2012 if (ret)
2013 return ret;
2014 } else if ((req->dst_rect.h % 32) == 3 ||
2015 ((req->dst_rect.h % 32) == 1 && req->dst_rect.h != 1) ||
2016 ((req->dst_rect.h % 32) == 2 && req->dst_rect.h != 2))
2017 ret = mdp_blit_split_height(info, req);
2018 else
2019 ret = mdp_ppp_blit(info, req);
2020 return ret;
2021#elif defined CONFIG_FB_MSM_MDP30
2022 /* MDP width split workaround */
2023 remainder = (req->dst_rect.w)%16;
2024 ret = mdp_get_bytes_per_pixel(req->dst.format,
2025 (struct msm_fb_data_type *)info->par);
2026 if (ret <= 0) {
2027 printk(KERN_ERR "mdp_ppp: incorrect bpp!\n");
2028 return -EINVAL;
2029 }
2030 is_bpp_4 = (ret == 4) ? 1 : 0;
2031
2032 if ((is_bpp_4 && (remainder == 6 || remainder == 14))) {
2033
2034 /* make new request as provide by user */
2035 splitreq = *req;
2036
2037 /* break dest roi at width*/
2038 d_y_0 = d_y_1 = req->dst_rect.y;
2039 d_h_0 = d_h_1 = req->dst_rect.h;
2040 d_x_0 = req->dst_rect.x;
2041
2042 if (remainder == 14 || remainder == 6)
2043 d_w_1 = req->dst_rect.w / 2;
2044 else
2045 d_w_1 = (req->dst_rect.w - 1) / 2 - 1;
2046
2047 d_w_0 = req->dst_rect.w - d_w_1;
2048 d_x_1 = d_x_0 + d_w_0;
2049
2050 /* blit first region */
2051 if (((splitreq.flags & 0x07) == 0x07) ||
2052 ((splitreq.flags & 0x07) == 0x0)) {
2053
2054 if (splitreq.flags & MDP_ROT_90) {
2055 s_x_0 = s_x_1 = req->src_rect.x;
2056 s_w_0 = s_w_1 = req->src_rect.w;
2057 s_y_0 = req->src_rect.y;
2058 s_h_1 = (req->src_rect.h * d_w_1) /
2059 req->dst_rect.w;
2060 s_h_0 = req->src_rect.h - s_h_1;
2061 s_y_1 = s_y_0 + s_h_0;
2062 if (d_w_1 >= 8 * s_h_1) {
2063 s_h_1++;
2064 s_y_1--;
2065 }
2066 } else {
2067 s_y_0 = s_y_1 = req->src_rect.y;
2068 s_h_0 = s_h_1 = req->src_rect.h;
2069 s_x_0 = req->src_rect.x;
2070 s_w_1 = (req->src_rect.w * d_w_1) /
2071 req->dst_rect.w;
2072 s_w_0 = req->src_rect.w - s_w_1;
2073 s_x_1 = s_x_0 + s_w_0;
2074 if (d_w_1 >= 8 * s_w_1) {
2075 s_w_1++;
2076 s_x_1--;
2077 }
2078 }
2079
2080 splitreq.src_rect.h = s_h_0;
2081 splitreq.src_rect.y = s_y_0;
2082 splitreq.dst_rect.h = d_h_0;
2083 splitreq.dst_rect.y = d_y_0;
2084 splitreq.src_rect.x = s_x_0;
2085 splitreq.src_rect.w = s_w_0;
2086 splitreq.dst_rect.x = d_x_0;
2087 splitreq.dst_rect.w = d_w_0;
2088 } else {
2089 if (splitreq.flags & MDP_ROT_90) {
2090 s_x_0 = s_x_1 = req->src_rect.x;
2091 s_w_0 = s_w_1 = req->src_rect.w;
2092 s_y_0 = req->src_rect.y;
2093 s_h_1 = (req->src_rect.h * d_w_0) /
2094 req->dst_rect.w;
2095 s_h_0 = req->src_rect.h - s_h_1;
2096 s_y_1 = s_y_0 + s_h_0;
2097 if (d_w_0 >= 8 * s_h_1) {
2098 s_h_1++;
2099 s_y_1--;
2100 }
2101 } else {
2102 s_y_0 = s_y_1 = req->src_rect.y;
2103 s_h_0 = s_h_1 = req->src_rect.h;
2104 s_x_0 = req->src_rect.x;
2105 s_w_1 = (req->src_rect.w * d_w_0) /
2106 req->dst_rect.w;
2107 s_w_0 = req->src_rect.w - s_w_1;
2108 s_x_1 = s_x_0 + s_w_0;
2109 if (d_w_0 >= 8 * s_w_1) {
2110 s_w_1++;
2111 s_x_1--;
2112 }
2113 }
2114 splitreq.src_rect.h = s_h_0;
2115 splitreq.src_rect.y = s_y_0;
2116 splitreq.dst_rect.h = d_h_1;
2117 splitreq.dst_rect.y = d_y_1;
2118 splitreq.src_rect.x = s_x_0;
2119 splitreq.src_rect.w = s_w_0;
2120 splitreq.dst_rect.x = d_x_1;
2121 splitreq.dst_rect.w = d_w_1;
2122 }
2123
2124 /* No need to split in height */
2125 ret = mdp_ppp_blit(info, &splitreq);
2126
2127 if (ret)
2128 return ret;
2129
2130 /* blit second region */
2131 if (((splitreq.flags & 0x07) == 0x07) ||
2132 ((splitreq.flags & 0x07) == 0x0)) {
2133 splitreq.src_rect.h = s_h_1;
2134 splitreq.src_rect.y = s_y_1;
2135 splitreq.dst_rect.h = d_h_1;
2136 splitreq.dst_rect.y = d_y_1;
2137 splitreq.src_rect.x = s_x_1;
2138 splitreq.src_rect.w = s_w_1;
2139 splitreq.dst_rect.x = d_x_1;
2140 splitreq.dst_rect.w = d_w_1;
2141 } else {
2142 splitreq.src_rect.h = s_h_1;
2143 splitreq.src_rect.y = s_y_1;
2144 splitreq.dst_rect.h = d_h_0;
2145 splitreq.dst_rect.y = d_y_0;
2146 splitreq.src_rect.x = s_x_1;
2147 splitreq.src_rect.w = s_w_1;
2148 splitreq.dst_rect.x = d_x_0;
2149 splitreq.dst_rect.w = d_w_0;
2150 }
2151
2152 /* No need to split in height ... just width */
2153 ret = mdp_ppp_blit(info, &splitreq);
2154
2155 if (ret)
2156 return ret;
2157
2158 } else
2159 ret = mdp_ppp_blit(info, req);
2160 return ret;
2161#else
2162 ret = mdp_ppp_blit(info, req);
2163 return ret;
2164#endif
2165}
2166
2167typedef void (*msm_dma_barrier_function_pointer) (void *, size_t);
2168
2169static inline void msm_fb_dma_barrier_for_rect(struct fb_info *info,
2170 struct mdp_img *img, struct mdp_rect *rect,
2171 msm_dma_barrier_function_pointer dma_barrier_fp
2172 )
2173{
2174 /*
2175 * Compute the start and end addresses of the rectangles.
2176 * NOTE: As currently implemented, the data between
2177 * the end of one row and the start of the next is
2178 * included in the address range rather than
2179 * doing multiple calls for each row.
2180 */
2181 unsigned long start;
2182 size_t size;
2183 char * const pmem_start = info->screen_base;
2184 int bytes_per_pixel = mdp_get_bytes_per_pixel(img->format,
2185 (struct msm_fb_data_type *)info->par);
2186 if (bytes_per_pixel <= 0) {
2187 printk(KERN_ERR "%s incorrect bpp!\n", __func__);
2188 return;
2189 }
2190 start = (unsigned long)pmem_start + img->offset +
2191 (img->width * rect->y + rect->x) * bytes_per_pixel;
2192 size = (rect->h * img->width + rect->w) * bytes_per_pixel;
2193 (*dma_barrier_fp) ((void *) start, size);
2194
2195}
2196
2197static inline void msm_dma_nc_pre(void)
2198{
2199 dmb();
2200}
2201static inline void msm_dma_wt_pre(void)
2202{
2203 dmb();
2204}
2205static inline void msm_dma_todevice_wb_pre(void *start, size_t size)
2206{
2207 dma_cache_pre_ops(start, size, DMA_TO_DEVICE);
2208}
2209
2210static inline void msm_dma_fromdevice_wb_pre(void *start, size_t size)
2211{
2212 dma_cache_pre_ops(start, size, DMA_FROM_DEVICE);
2213}
2214
2215static inline void msm_dma_nc_post(void)
2216{
2217 dmb();
2218}
2219
2220static inline void msm_dma_fromdevice_wt_post(void *start, size_t size)
2221{
2222 dma_cache_post_ops(start, size, DMA_FROM_DEVICE);
2223}
2224
2225static inline void msm_dma_todevice_wb_post(void *start, size_t size)
2226{
2227 dma_cache_post_ops(start, size, DMA_TO_DEVICE);
2228}
2229
2230static inline void msm_dma_fromdevice_wb_post(void *start, size_t size)
2231{
2232 dma_cache_post_ops(start, size, DMA_FROM_DEVICE);
2233}
2234
2235/*
2236 * Do the write barriers required to guarantee data is committed to RAM
2237 * (from CPU cache or internal buffers) before a DMA operation starts.
2238 * NOTE: As currently implemented, the data between
2239 * the end of one row and the start of the next is
2240 * included in the address range rather than
2241 * doing multiple calls for each row.
2242*/
2243static void msm_fb_ensure_memory_coherency_before_dma(struct fb_info *info,
2244 struct mdp_blit_req *req_list,
2245 int req_list_count)
2246{
2247#ifdef CONFIG_ARCH_QSD8X50
2248 int i;
2249
2250 /*
2251 * Normally, do the requested barriers for each address
2252 * range that corresponds to a rectangle.
2253 *
2254 * But if at least one write barrier is requested for data
2255 * going to or from the device but no address range is
2256 * needed for that barrier, then do the barrier, but do it
2257 * only once, no matter how many requests there are.
2258 */
2259 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
2260 switch (mfd->mdp_fb_page_protection) {
2261 default:
2262 case MDP_FB_PAGE_PROTECTION_NONCACHED:
2263 case MDP_FB_PAGE_PROTECTION_WRITECOMBINE:
2264 /*
2265 * The following barrier is only done at most once,
2266 * since further calls would be redundant.
2267 */
2268 for (i = 0; i < req_list_count; i++) {
2269 if (!(req_list[i].flags
2270 & MDP_NO_DMA_BARRIER_START)) {
2271 msm_dma_nc_pre();
2272 break;
2273 }
2274 }
2275 break;
2276
2277 case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE:
2278 /*
2279 * The following barrier is only done at most once,
2280 * since further calls would be redundant.
2281 */
2282 for (i = 0; i < req_list_count; i++) {
2283 if (!(req_list[i].flags
2284 & MDP_NO_DMA_BARRIER_START)) {
2285 msm_dma_wt_pre();
2286 break;
2287 }
2288 }
2289 break;
2290
2291 case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE:
2292 case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE:
2293 for (i = 0; i < req_list_count; i++) {
2294 if (!(req_list[i].flags &
2295 MDP_NO_DMA_BARRIER_START)) {
2296
2297 msm_fb_dma_barrier_for_rect(info,
2298 &(req_list[i].src),
2299 &(req_list[i].src_rect),
2300 msm_dma_todevice_wb_pre
2301 );
2302
2303 msm_fb_dma_barrier_for_rect(info,
2304 &(req_list[i].dst),
2305 &(req_list[i].dst_rect),
2306 msm_dma_todevice_wb_pre
2307 );
2308 }
2309 }
2310 break;
2311 }
2312#else
2313 dmb();
2314#endif
2315}
2316
2317
2318/*
2319 * Do the write barriers required to guarantee data will be re-read from RAM by
2320 * the CPU after a DMA operation ends.
2321 * NOTE: As currently implemented, the data between
2322 * the end of one row and the start of the next is
2323 * included in the address range rather than
2324 * doing multiple calls for each row.
2325*/
2326static void msm_fb_ensure_memory_coherency_after_dma(struct fb_info *info,
2327 struct mdp_blit_req *req_list,
2328 int req_list_count)
2329{
2330#ifdef CONFIG_ARCH_QSD8X50
2331 int i;
2332
2333 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
2334 switch (mfd->mdp_fb_page_protection) {
2335 default:
2336 case MDP_FB_PAGE_PROTECTION_NONCACHED:
2337 case MDP_FB_PAGE_PROTECTION_WRITECOMBINE:
2338 /*
2339 * The following barrier is only done at most once,
2340 * since further calls would be redundant.
2341 */
2342 for (i = 0; i < req_list_count; i++) {
2343 if (!(req_list[i].flags
2344 & MDP_NO_DMA_BARRIER_END)) {
2345 msm_dma_nc_post();
2346 break;
2347 }
2348 }
2349 break;
2350
2351 case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE:
2352 for (i = 0; i < req_list_count; i++) {
2353 if (!(req_list[i].flags &
2354 MDP_NO_DMA_BARRIER_END)) {
2355
2356 msm_fb_dma_barrier_for_rect(info,
2357 &(req_list[i].dst),
2358 &(req_list[i].dst_rect),
2359 msm_dma_fromdevice_wt_post
2360 );
2361 }
2362 }
2363 break;
2364 case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE:
2365 case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE:
2366 for (i = 0; i < req_list_count; i++) {
2367 if (!(req_list[i].flags &
2368 MDP_NO_DMA_BARRIER_END)) {
2369
2370 msm_fb_dma_barrier_for_rect(info,
2371 &(req_list[i].dst),
2372 &(req_list[i].dst_rect),
2373 msm_dma_fromdevice_wb_post
2374 );
2375 }
2376 }
2377 break;
2378 }
2379#else
2380 dmb();
2381#endif
2382}
2383
2384/*
2385 * NOTE: The userspace issues blit operations in a sequence, the sequence
2386 * start with a operation marked START and ends in an operation marked
2387 * END. It is guranteed by the userspace that all the blit operations
2388 * between START and END are only within the regions of areas designated
2389 * by the START and END operations and that the userspace doesnt modify
2390 * those areas. Hence it would be enough to perform barrier/cache operations
2391 * only on the START and END operations.
2392 */
2393static int msmfb_blit(struct fb_info *info, void __user *p)
2394{
2395 /*
2396 * CAUTION: The names of the struct types intentionally *DON'T* match
2397 * the names of the variables declared -- they appear to be swapped.
2398 * Read the code carefully and you should see that the variable names
2399 * make sense.
2400 */
2401 const int MAX_LIST_WINDOW = 16;
2402 struct mdp_blit_req req_list[MAX_LIST_WINDOW];
2403 struct mdp_blit_req_list req_list_header;
2404
2405 int count, i, req_list_count;
2406
2407 /* Get the count size for the total BLIT request. */
2408 if (copy_from_user(&req_list_header, p, sizeof(req_list_header)))
2409 return -EFAULT;
2410 p += sizeof(req_list_header);
2411 count = req_list_header.count;
2412 if (count < 0 || count >= MAX_BLIT_REQ)
2413 return -EINVAL;
2414 while (count > 0) {
2415 /*
2416 * Access the requests through a narrow window to decrease copy
2417 * overhead and make larger requests accessible to the
2418 * coherency management code.
2419 * NOTE: The window size is intended to be larger than the
2420 * typical request size, but not require more than 2
2421 * kbytes of stack storage.
2422 */
2423 req_list_count = count;
2424 if (req_list_count > MAX_LIST_WINDOW)
2425 req_list_count = MAX_LIST_WINDOW;
2426 if (copy_from_user(&req_list, p,
2427 sizeof(struct mdp_blit_req)*req_list_count))
2428 return -EFAULT;
2429
2430 /*
2431 * Ensure that any data CPU may have previously written to
2432 * internal state (but not yet committed to memory) is
2433 * guaranteed to be committed to memory now.
2434 */
2435 msm_fb_ensure_memory_coherency_before_dma(info,
2436 req_list, req_list_count);
2437
2438 /*
2439 * Do the blit DMA, if required -- returning early only if
2440 * there is a failure.
2441 */
2442 for (i = 0; i < req_list_count; i++) {
2443 if (!(req_list[i].flags & MDP_NO_BLIT)) {
2444 /* Do the actual blit. */
2445 int ret = mdp_blit(info, &(req_list[i]));
2446
2447 /*
2448 * Note that early returns don't guarantee
2449 * memory coherency.
2450 */
2451 if (ret)
2452 return ret;
2453 }
2454 }
2455
2456 /*
2457 * Ensure that CPU cache and other internal CPU state is
2458 * updated to reflect any change in memory modified by MDP blit
2459 * DMA.
2460 */
2461 msm_fb_ensure_memory_coherency_after_dma(info,
2462 req_list,
2463 req_list_count);
2464
2465 /* Go to next window of requests. */
2466 count -= req_list_count;
2467 p += sizeof(struct mdp_blit_req)*req_list_count;
2468 }
2469 return 0;
2470}
2471
2472#ifdef CONFIG_FB_MSM_OVERLAY
2473static int msmfb_overlay_get(struct fb_info *info, void __user *p)
2474{
2475 struct mdp_overlay req;
2476 int ret;
2477
2478 if (copy_from_user(&req, p, sizeof(req)))
2479 return -EFAULT;
2480
2481 ret = mdp4_overlay_get(info, &req);
2482 if (ret) {
2483 printk(KERN_ERR "%s: ioctl failed \n",
2484 __func__);
2485 return ret;
2486 }
2487 if (copy_to_user(p, &req, sizeof(req))) {
2488 printk(KERN_ERR "%s: copy2user failed \n",
2489 __func__);
2490 return -EFAULT;
2491 }
2492
2493 return 0;
2494}
2495
2496static int msmfb_overlay_set(struct fb_info *info, void __user *p)
2497{
2498 struct mdp_overlay req;
2499 int ret;
2500
2501 if (copy_from_user(&req, p, sizeof(req)))
2502 return -EFAULT;
2503
2504 ret = mdp4_overlay_set(info, &req);
2505 if (ret) {
2506 printk(KERN_ERR "%s: ioctl failed, rc=%d\n",
2507 __func__, ret);
2508 return ret;
2509 }
2510
2511 if (copy_to_user(p, &req, sizeof(req))) {
2512 printk(KERN_ERR "%s: copy2user failed \n",
2513 __func__);
2514 return -EFAULT;
2515 }
2516
2517 return 0;
2518}
2519
2520static int msmfb_overlay_unset(struct fb_info *info, unsigned long *argp)
2521{
2522 int ret, ndx;
2523
2524 ret = copy_from_user(&ndx, argp, sizeof(ndx));
2525 if (ret) {
2526 printk(KERN_ERR "%s:msmfb_overlay_unset ioctl failed \n",
2527 __func__);
2528 return ret;
2529 }
2530
2531 return mdp4_overlay_unset(info, ndx);
2532}
2533
Nagamalleswararao Ganji0737d652011-10-14 02:02:33 -07002534static int msmfb_overlay_play_wait(struct fb_info *info, unsigned long *argp)
2535{
2536 int ret;
2537 struct msmfb_overlay_data req;
2538 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
2539
2540 if (mfd->overlay_play_enable == 0) /* nothing to do */
2541 return 0;
2542
2543 ret = copy_from_user(&req, argp, sizeof(req));
2544 if (ret) {
2545 pr_err("%s:msmfb_overlay_wait ioctl failed", __func__);
2546 return ret;
2547 }
2548
2549 ret = mdp4_overlay_play_wait(info, &req);
2550
2551 return ret;
2552}
2553
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002554static int msmfb_overlay_play(struct fb_info *info, unsigned long *argp)
2555{
2556 int ret;
2557 struct msmfb_overlay_data req;
2558 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002559
2560 if (mfd->overlay_play_enable == 0) /* nothing to do */
2561 return 0;
2562
2563 ret = copy_from_user(&req, argp, sizeof(req));
2564 if (ret) {
2565 printk(KERN_ERR "%s:msmfb_overlay_play ioctl failed \n",
2566 __func__);
2567 return ret;
2568 }
2569
2570 complete(&mfd->msmfb_update_notify);
2571 mutex_lock(&msm_fb_notify_update_sem);
2572 if (mfd->msmfb_no_update_notify_timer.function)
2573 del_timer(&mfd->msmfb_no_update_notify_timer);
2574
2575 mfd->msmfb_no_update_notify_timer.expires =
2576 jiffies + ((1000 * HZ) / 1000);
2577 add_timer(&mfd->msmfb_no_update_notify_timer);
2578 mutex_unlock(&msm_fb_notify_update_sem);
2579
Nagamalleswararao Ganjie69bed82011-10-17 16:21:42 -07002580 ret = mdp4_overlay_play(info, &req);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002581
2582 return ret;
2583}
2584
2585static int msmfb_overlay_play_enable(struct fb_info *info, unsigned long *argp)
2586{
2587 int ret, enable;
2588 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
2589
2590 ret = copy_from_user(&enable, argp, sizeof(enable));
2591 if (ret) {
2592 printk(KERN_ERR "%s:msmfb_overlay_play_enable ioctl failed \n",
2593 __func__);
2594 return ret;
2595 }
2596
2597 mfd->overlay_play_enable = enable;
2598
2599 return 0;
2600}
2601
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002602static int msmfb_overlay_blt(struct fb_info *info, unsigned long *argp)
2603{
2604 int ret;
2605 struct msmfb_overlay_blt req;
2606
2607 ret = copy_from_user(&req, argp, sizeof(req));
2608 if (ret) {
kuogee hsieh405dc302011-07-21 15:06:59 -07002609 pr_err("%s: failed\n", __func__);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002610 return ret;
2611 }
2612
2613 ret = mdp4_overlay_blt(info, &req);
2614
2615 return ret;
2616}
2617
2618static int msmfb_overlay_blt_off(struct fb_info *info, unsigned long *argp)
2619{
2620 int ret;
2621 struct msmfb_overlay_blt req;
2622
kuogee hsieh405dc302011-07-21 15:06:59 -07002623 ret = copy_from_user(&req, argp, sizeof(req));
2624 if (ret) {
2625 pr_err("%s: failed\n", __func__);
2626 return ret;
2627 }
2628
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002629 ret = mdp4_overlay_blt_offset(info, &req);
2630
2631 ret = copy_to_user(argp, &req, sizeof(req));
2632 if (ret)
2633 printk(KERN_ERR "%s:msmfb_overlay_blt_off ioctl failed\n",
2634 __func__);
2635
2636 return ret;
2637}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002638
Vinay Kalia27020d12011-10-14 17:50:29 -07002639#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
2640static int msmfb_overlay_ioctl_writeback_init(struct fb_info *info)
2641{
2642 return mdp4_writeback_init(info);
2643}
2644static int msmfb_overlay_ioctl_writeback_register_buffer(
2645 struct fb_info *info, unsigned long *argp)
2646{
2647 int ret = 0;
2648 struct msmfb_writeback_data data;
2649
2650 ret = copy_from_user(&data, argp, sizeof(data));
2651 if (ret)
2652 goto error;
2653
2654 ret = mdp4_writeback_register_buffer(info, &data);
2655 if (ret)
2656 goto error;
2657error:
2658 if (ret)
2659 pr_err("%s:msmfb_writeback_register_buffer "
2660 " ioctl failed\n", __func__);
2661 return ret;
2662}
2663
2664static int msmfb_overlay_ioctl_writeback_unregister_buffer(
2665 struct fb_info *info, unsigned long *argp)
2666{
2667 int ret = 0;
2668 struct msmfb_writeback_data data;
2669
2670 ret = copy_from_user(&data, argp, sizeof(data));
2671 if (ret)
2672 goto error;
2673
2674 ret = mdp4_writeback_unregister_buffer(info, &data);
2675 if (ret)
2676 goto error;
2677
2678error:
2679 if (ret)
2680 pr_err("%s:msmfb_writeback_unregister_buffer ioctl failed\n",
2681 __func__);
2682 return ret;
2683}
2684
2685static int msmfb_overlay_ioctl_writeback_queue_buffer(
2686 struct fb_info *info, unsigned long *argp)
2687{
2688 int ret = 0;
2689 struct msmfb_data data;
2690
2691 ret = copy_from_user(&data, argp, sizeof(data));
2692 if (ret)
2693 goto error;
2694
2695 ret = mdp4_writeback_queue_buffer(info, &data);
2696 if (ret)
2697 goto error;
2698
2699error:
2700 if (ret)
2701 pr_err("%s:msmfb_writeback_queue_buffer ioctl failed\n",
2702 __func__);
2703 return ret;
2704}
2705
2706static int msmfb_overlay_ioctl_writeback_dequeue_buffer(
2707 struct fb_info *info, unsigned long *argp)
2708{
2709 int ret = 0;
2710 struct msmfb_data data;
2711
2712 ret = copy_from_user(&data, argp, sizeof(data));
2713 if (ret)
2714 goto error;
2715
2716 ret = mdp4_writeback_dequeue_buffer(info, &data);
2717 if (ret)
2718 goto error;
2719
2720 ret = copy_to_user(argp, &data, sizeof(data));
2721 if (ret)
2722 goto error;
2723
2724error:
2725 if (ret)
2726 pr_err("%s:msmfb_writeback_dequeue_buffer ioctl failed\n",
2727 __func__);
2728 return ret;
2729}
2730static int msmfb_overlay_ioctl_writeback_terminate(struct fb_info *info)
2731{
2732 return mdp4_writeback_terminate(info);
2733}
2734
2735#else
2736static int msmfb_overlay_ioctl_writeback_init(struct fb_info *info)
2737{
2738 return -ENOTSUPP;
2739}
2740static int msmfb_overlay_ioctl_writeback_register_buffer(
2741 struct fb_info *info, unsigned long *argp)
2742{
2743 return -ENOTSUPP;
2744}
2745
2746static int msmfb_overlay_ioctl_writeback_unregister_buffer(
2747 struct fb_info *info, unsigned long *argp)
2748{
2749 return -ENOTSUPP;
2750}
2751
2752static int msmfb_overlay_ioctl_writeback_queue_buffer(
2753 struct fb_info *info, unsigned long *argp)
2754{
2755 return -ENOTSUPP;
2756}
2757
2758static int msmfb_overlay_ioctl_writeback_dequeue_buffer(
2759 struct fb_info *info, unsigned long *argp)
2760{
2761 return -ENOTSUPP;
2762}
2763static int msmfb_overlay_ioctl_writeback_terminate(struct fb_info *info)
2764{
2765 return -ENOTSUPP;
2766}
2767#endif
2768
kuogee hsieh4aea2742011-07-06 11:05:05 -07002769static int msmfb_overlay_3d_sbys(struct fb_info *info, unsigned long *argp)
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002770{
2771 int ret;
2772 struct msmfb_overlay_3d req;
2773
2774 ret = copy_from_user(&req, argp, sizeof(req));
2775 if (ret) {
2776 pr_err("%s:msmfb_overlay_3d_ctrl ioctl failed\n",
2777 __func__);
2778 return ret;
2779 }
2780
kuogee hsieh4aea2742011-07-06 11:05:05 -07002781 ret = mdp4_overlay_3d_sbys(info, &req);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002782
2783 return ret;
2784}
2785
kuogee hsieh405dc302011-07-21 15:06:59 -07002786static int msmfb_mixer_info(struct fb_info *info, unsigned long *argp)
2787{
2788 int ret, cnt;
2789 struct msmfb_mixer_info_req req;
2790
2791 ret = copy_from_user(&req, argp, sizeof(req));
2792 if (ret) {
2793 pr_err("%s: failed\n", __func__);
2794 return ret;
2795 }
2796
2797 cnt = mdp4_mixer_info(req.mixer_num, req.info);
2798 req.cnt = cnt;
2799 ret = copy_to_user(argp, &req, sizeof(req));
2800 if (ret)
2801 pr_err("%s:msmfb_overlay_blt_off ioctl failed\n",
2802 __func__);
2803
2804 return cnt;
2805}
2806
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002807#endif
2808
2809DEFINE_SEMAPHORE(msm_fb_ioctl_ppp_sem);
2810DEFINE_MUTEX(msm_fb_ioctl_lut_sem);
2811DEFINE_MUTEX(msm_fb_ioctl_hist_sem);
2812
2813/* Set color conversion matrix from user space */
2814
2815#ifndef CONFIG_FB_MSM_MDP40
2816static void msmfb_set_color_conv(struct mdp_ccs *p)
2817{
2818 int i;
2819
2820 if (p->direction == MDP_CCS_RGB2YUV) {
2821 /* MDP cmd block enable */
2822 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
2823
2824 /* RGB->YUV primary forward matrix */
2825 for (i = 0; i < MDP_CCS_SIZE; i++)
2826 writel(p->ccs[i], MDP_CSC_PFMVn(i));
2827
2828 #ifdef CONFIG_FB_MSM_MDP31
2829 for (i = 0; i < MDP_BV_SIZE; i++)
2830 writel(p->bv[i], MDP_CSC_POST_BV2n(i));
2831 #endif
2832
2833 /* MDP cmd block disable */
2834 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
2835 } else {
2836 /* MDP cmd block enable */
2837 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
2838
2839 /* YUV->RGB primary reverse matrix */
2840 for (i = 0; i < MDP_CCS_SIZE; i++)
2841 writel(p->ccs[i], MDP_CSC_PRMVn(i));
2842 for (i = 0; i < MDP_BV_SIZE; i++)
2843 writel(p->bv[i], MDP_CSC_PRE_BV1n(i));
2844
2845 /* MDP cmd block disable */
2846 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF, FALSE);
2847 }
2848}
Nagamalleswararao Ganji4b991722011-01-28 13:24:34 -08002849#else
2850static void msmfb_set_color_conv(struct mdp_csc *p)
2851{
2852 mdp4_vg_csc_update(p);
2853}
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002854#endif
2855
2856static int msmfb_notify_update(struct fb_info *info, unsigned long *argp)
2857{
2858 int ret, notify;
2859 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
2860
2861 ret = copy_from_user(&notify, argp, sizeof(int));
2862 if (ret) {
2863 pr_err("%s:ioctl failed\n", __func__);
2864 return ret;
2865 }
2866
2867 if (notify > NOTIFY_UPDATE_STOP)
2868 return -EINVAL;
2869
2870 if (notify == NOTIFY_UPDATE_START) {
2871 INIT_COMPLETION(mfd->msmfb_update_notify);
2872 wait_for_completion_interruptible(&mfd->msmfb_update_notify);
2873 } else {
2874 INIT_COMPLETION(mfd->msmfb_no_update_notify);
2875 wait_for_completion_interruptible(&mfd->msmfb_no_update_notify);
2876 }
2877 return 0;
2878}
2879
2880static int msm_fb_ioctl(struct fb_info *info, unsigned int cmd,
2881 unsigned long arg)
2882{
2883 struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par;
2884 void __user *argp = (void __user *)arg;
2885 struct fb_cursor cursor;
2886 struct fb_cmap cmap;
2887 struct mdp_histogram hist;
2888#ifndef CONFIG_FB_MSM_MDP40
2889 struct mdp_ccs ccs_matrix;
Nagamalleswararao Ganji4b991722011-01-28 13:24:34 -08002890#else
2891 struct mdp_csc csc_matrix;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002892#endif
2893 struct mdp_page_protection fb_page_protection;
2894 int ret = 0;
2895
2896 switch (cmd) {
2897#ifdef CONFIG_FB_MSM_OVERLAY
2898 case MSMFB_OVERLAY_GET:
2899 down(&msm_fb_ioctl_ppp_sem);
2900 ret = msmfb_overlay_get(info, argp);
2901 up(&msm_fb_ioctl_ppp_sem);
2902 break;
2903 case MSMFB_OVERLAY_SET:
2904 down(&msm_fb_ioctl_ppp_sem);
2905 ret = msmfb_overlay_set(info, argp);
2906 up(&msm_fb_ioctl_ppp_sem);
2907 break;
2908 case MSMFB_OVERLAY_UNSET:
2909 down(&msm_fb_ioctl_ppp_sem);
2910 ret = msmfb_overlay_unset(info, argp);
2911 up(&msm_fb_ioctl_ppp_sem);
2912 break;
2913 case MSMFB_OVERLAY_PLAY:
2914 down(&msm_fb_ioctl_ppp_sem);
2915 ret = msmfb_overlay_play(info, argp);
2916 up(&msm_fb_ioctl_ppp_sem);
2917 break;
2918 case MSMFB_OVERLAY_PLAY_ENABLE:
2919 down(&msm_fb_ioctl_ppp_sem);
2920 ret = msmfb_overlay_play_enable(info, argp);
2921 up(&msm_fb_ioctl_ppp_sem);
2922 break;
Nagamalleswararao Ganji0737d652011-10-14 02:02:33 -07002923 case MSMFB_OVERLAY_PLAY_WAIT:
2924 down(&msm_fb_ioctl_ppp_sem);
2925 ret = msmfb_overlay_play_wait(info, argp);
2926 up(&msm_fb_ioctl_ppp_sem);
2927 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002928 case MSMFB_OVERLAY_BLT:
2929 down(&msm_fb_ioctl_ppp_sem);
2930 ret = msmfb_overlay_blt(info, argp);
2931 up(&msm_fb_ioctl_ppp_sem);
2932 break;
2933 case MSMFB_OVERLAY_BLT_OFFSET:
2934 down(&msm_fb_ioctl_ppp_sem);
2935 ret = msmfb_overlay_blt_off(info, argp);
2936 up(&msm_fb_ioctl_ppp_sem);
2937 break;
2938 case MSMFB_OVERLAY_3D:
2939 down(&msm_fb_ioctl_ppp_sem);
kuogee hsieh4aea2742011-07-06 11:05:05 -07002940 ret = msmfb_overlay_3d_sbys(info, argp);
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002941 up(&msm_fb_ioctl_ppp_sem);
2942 break;
kuogee hsieh405dc302011-07-21 15:06:59 -07002943 case MSMFB_MIXER_INFO:
2944 down(&msm_fb_ioctl_ppp_sem);
2945 ret = msmfb_mixer_info(info, argp);
2946 up(&msm_fb_ioctl_ppp_sem);
2947 break;
Vinay Kalia27020d12011-10-14 17:50:29 -07002948 case MSMFB_WRITEBACK_INIT:
2949 ret = msmfb_overlay_ioctl_writeback_init(info);
2950 break;
2951 case MSMFB_WRITEBACK_REGISTER_BUFFER:
2952 ret = msmfb_overlay_ioctl_writeback_register_buffer(
2953 info, argp);
2954 break;
2955 case MSMFB_WRITEBACK_UNREGISTER_BUFFER:
2956 ret = msmfb_overlay_ioctl_writeback_unregister_buffer(
2957 info, argp);
2958 break;
2959 case MSMFB_WRITEBACK_QUEUE_BUFFER:
2960 ret = msmfb_overlay_ioctl_writeback_queue_buffer(
2961 info, argp);
2962 break;
2963 case MSMFB_WRITEBACK_DEQUEUE_BUFFER:
2964 ret = msmfb_overlay_ioctl_writeback_dequeue_buffer(
2965 info, argp);
2966 break;
2967 case MSMFB_WRITEBACK_TERMINATE:
2968 ret = msmfb_overlay_ioctl_writeback_terminate(info);
2969 break;
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07002970#endif
2971 case MSMFB_BLIT:
2972 down(&msm_fb_ioctl_ppp_sem);
2973 ret = msmfb_blit(info, argp);
2974 up(&msm_fb_ioctl_ppp_sem);
2975
2976 break;
2977
2978 /* Ioctl for setting ccs matrix from user space */
2979 case MSMFB_SET_CCS_MATRIX:
2980#ifndef CONFIG_FB_MSM_MDP40
2981 ret = copy_from_user(&ccs_matrix, argp, sizeof(ccs_matrix));
2982 if (ret) {
2983 printk(KERN_ERR
2984 "%s:MSMFB_SET_CCS_MATRIX ioctl failed \n",
2985 __func__);
2986 return ret;
2987 }
2988
2989 down(&msm_fb_ioctl_ppp_sem);
2990 if (ccs_matrix.direction == MDP_CCS_RGB2YUV)
2991 mdp_ccs_rgb2yuv = ccs_matrix;
2992 else
2993 mdp_ccs_yuv2rgb = ccs_matrix;
2994
2995 msmfb_set_color_conv(&ccs_matrix) ;
2996 up(&msm_fb_ioctl_ppp_sem);
2997#else
Nagamalleswararao Ganji4b991722011-01-28 13:24:34 -08002998 ret = copy_from_user(&csc_matrix, argp, sizeof(csc_matrix));
2999 if (ret) {
3000 pr_err("%s:MSMFB_SET_CSC_MATRIX ioctl failed\n",
3001 __func__);
3002 return ret;
3003 }
3004 down(&msm_fb_ioctl_ppp_sem);
3005 msmfb_set_color_conv(&csc_matrix);
3006 up(&msm_fb_ioctl_ppp_sem);
3007
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003008#endif
3009
3010 break;
3011
3012 /* Ioctl for getting ccs matrix to user space */
3013 case MSMFB_GET_CCS_MATRIX:
3014#ifndef CONFIG_FB_MSM_MDP40
3015 ret = copy_from_user(&ccs_matrix, argp, sizeof(ccs_matrix)) ;
3016 if (ret) {
3017 printk(KERN_ERR
3018 "%s:MSMFB_GET_CCS_MATRIX ioctl failed \n",
3019 __func__);
3020 return ret;
3021 }
3022
3023 down(&msm_fb_ioctl_ppp_sem);
3024 if (ccs_matrix.direction == MDP_CCS_RGB2YUV)
3025 ccs_matrix = mdp_ccs_rgb2yuv;
3026 else
3027 ccs_matrix = mdp_ccs_yuv2rgb;
3028
3029 ret = copy_to_user(argp, &ccs_matrix, sizeof(ccs_matrix));
3030
3031 if (ret) {
3032 printk(KERN_ERR
3033 "%s:MSMFB_GET_CCS_MATRIX ioctl failed \n",
3034 __func__);
3035 return ret ;
3036 }
3037 up(&msm_fb_ioctl_ppp_sem);
3038#else
3039 ret = -EINVAL;
3040#endif
3041
3042 break;
3043
3044 case MSMFB_GRP_DISP:
3045#ifdef CONFIG_FB_MSM_MDP22
3046 {
3047 unsigned long grp_id;
3048
3049 ret = copy_from_user(&grp_id, argp, sizeof(grp_id));
3050 if (ret)
3051 return ret;
3052
3053 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_ON, FALSE);
3054 writel(grp_id, MDP_FULL_BYPASS_WORD43);
3055 mdp_pipe_ctrl(MDP_CMD_BLOCK, MDP_BLOCK_POWER_OFF,
3056 FALSE);
3057 break;
3058 }
3059#else
3060 return -EFAULT;
3061#endif
3062 case MSMFB_SUSPEND_SW_REFRESHER:
3063 if (!mfd->panel_power_on)
3064 return -EPERM;
3065
3066 mfd->sw_refreshing_enable = FALSE;
3067 ret = msm_fb_stop_sw_refresher(mfd);
3068 break;
3069
3070 case MSMFB_RESUME_SW_REFRESHER:
3071 if (!mfd->panel_power_on)
3072 return -EPERM;
3073
3074 mfd->sw_refreshing_enable = TRUE;
3075 ret = msm_fb_resume_sw_refresher(mfd);
3076 break;
3077
3078 case MSMFB_CURSOR:
3079 ret = copy_from_user(&cursor, argp, sizeof(cursor));
3080 if (ret)
3081 return ret;
3082
3083 ret = msm_fb_cursor(info, &cursor);
3084 break;
3085
3086 case MSMFB_SET_LUT:
3087 ret = copy_from_user(&cmap, argp, sizeof(cmap));
3088 if (ret)
3089 return ret;
3090
3091 mutex_lock(&msm_fb_ioctl_lut_sem);
3092 ret = msm_fb_set_lut(&cmap, info);
3093 mutex_unlock(&msm_fb_ioctl_lut_sem);
3094 break;
3095
3096 case MSMFB_HISTOGRAM:
Carl Vanderlip74845152011-10-11 10:24:32 -07003097 if (!mfd->panel_power_on)
3098 return -EPERM;
3099
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003100 if (!mfd->do_histogram)
3101 return -ENODEV;
3102
3103 ret = copy_from_user(&hist, argp, sizeof(hist));
3104 if (ret)
3105 return ret;
3106
3107 mutex_lock(&msm_fb_ioctl_hist_sem);
3108 ret = mfd->do_histogram(info, &hist);
3109 mutex_unlock(&msm_fb_ioctl_hist_sem);
3110 break;
3111
3112 case MSMFB_HISTOGRAM_START:
3113 if (!mfd->do_histogram)
3114 return -ENODEV;
3115 ret = mdp_start_histogram(info);
3116 break;
3117
3118 case MSMFB_HISTOGRAM_STOP:
3119 if (!mfd->do_histogram)
3120 return -ENODEV;
3121 ret = mdp_stop_histogram(info);
3122 break;
3123
3124
3125 case MSMFB_GET_PAGE_PROTECTION:
3126 fb_page_protection.page_protection
3127 = mfd->mdp_fb_page_protection;
3128 ret = copy_to_user(argp, &fb_page_protection,
3129 sizeof(fb_page_protection));
3130 if (ret)
3131 return ret;
3132 break;
3133
3134 case MSMFB_NOTIFY_UPDATE:
3135 ret = msmfb_notify_update(info, argp);
3136 break;
3137
3138 case MSMFB_SET_PAGE_PROTECTION:
3139#if defined CONFIG_ARCH_QSD8X50 || defined CONFIG_ARCH_MSM8X60
3140 ret = copy_from_user(&fb_page_protection, argp,
3141 sizeof(fb_page_protection));
3142 if (ret)
3143 return ret;
3144
3145 /* Validate the proposed page protection settings. */
3146 switch (fb_page_protection.page_protection) {
3147 case MDP_FB_PAGE_PROTECTION_NONCACHED:
3148 case MDP_FB_PAGE_PROTECTION_WRITECOMBINE:
3149 case MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE:
3150 /* Write-back cache (read allocate) */
3151 case MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE:
3152 /* Write-back cache (write allocate) */
3153 case MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE:
3154 mfd->mdp_fb_page_protection =
3155 fb_page_protection.page_protection;
3156 break;
3157 default:
3158 ret = -EINVAL;
3159 break;
3160 }
3161#else
3162 /*
3163 * Don't allow caching until 7k DMA cache operations are
3164 * available.
3165 */
3166 ret = -EINVAL;
3167#endif
3168 break;
3169
3170 default:
3171 MSM_FB_INFO("MDP: unknown ioctl (cmd=%x) received!\n", cmd);
3172 ret = -EINVAL;
3173 break;
3174 }
3175
3176 return ret;
3177}
3178
3179static int msm_fb_register_driver(void)
3180{
3181 return platform_driver_register(&msm_fb_driver);
3182}
3183
Vinay Kalia27020d12011-10-14 17:50:29 -07003184#ifdef CONFIG_FB_MSM_WRITEBACK_MSM_PANEL
3185struct fb_info *msm_fb_get_writeback_fb(void)
3186{
3187 int c = 0;
3188 for (c = 0; c < fbi_list_index; ++c) {
3189 struct msm_fb_data_type *mfd;
3190 mfd = (struct msm_fb_data_type *)fbi_list[c]->par;
3191 if (mfd->panel.type == WRITEBACK_PANEL)
3192 return fbi_list[c];
3193 }
3194
3195 return NULL;
3196}
3197EXPORT_SYMBOL(msm_fb_get_writeback_fb);
3198
3199int msm_fb_writeback_register_buffer(struct fb_info *info,
3200 struct msmfb_writeback_data *data)
3201{
3202 return mdp4_writeback_register_buffer(info, data);
3203}
3204EXPORT_SYMBOL(msm_fb_writeback_register_buffer);
3205
3206int msm_fb_writeback_queue_buffer(struct fb_info *info,
3207 struct msmfb_data *data)
3208{
3209 return mdp4_writeback_queue_buffer(info, data);
3210}
3211EXPORT_SYMBOL(msm_fb_writeback_queue_buffer);
3212
3213int msm_fb_writeback_dequeue_buffer(struct fb_info *info,
3214 struct msmfb_data *data)
3215{
3216 return mdp4_writeback_dequeue_buffer(info, data);
3217}
3218EXPORT_SYMBOL(msm_fb_writeback_dequeue_buffer);
3219
3220int msm_fb_writeback_unregister_buffer(struct fb_info *info,
3221 struct msmfb_writeback_data *data)
3222{
3223 return mdp4_writeback_unregister_buffer(info, data);
3224}
3225EXPORT_SYMBOL(msm_fb_writeback_unregister_buffer);
3226int msm_fb_writeback_init(struct fb_info *info)
3227{
3228 return mdp4_writeback_init(info);
3229}
3230EXPORT_SYMBOL(msm_fb_writeback_init);
3231int msm_fb_writeback_terminate(struct fb_info *info)
3232{
3233 return mdp4_writeback_terminate(info);
3234}
3235EXPORT_SYMBOL(msm_fb_writeback_terminate);
3236#endif
3237
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003238struct platform_device *msm_fb_add_device(struct platform_device *pdev)
3239{
3240 struct msm_fb_panel_data *pdata;
3241 struct platform_device *this_dev = NULL;
3242 struct fb_info *fbi;
3243 struct msm_fb_data_type *mfd = NULL;
3244 u32 type, id, fb_num;
3245
3246 if (!pdev)
3247 return NULL;
3248 id = pdev->id;
3249
3250 pdata = pdev->dev.platform_data;
3251 if (!pdata)
3252 return NULL;
3253 type = pdata->panel_info.type;
3254
3255#if defined MSM_FB_NUM
3256 /*
3257 * over written fb_num which defined
3258 * at panel_info
3259 *
3260 */
Vinay Kalia27020d12011-10-14 17:50:29 -07003261 if (type == HDMI_PANEL || type == DTV_PANEL ||
3262 type == TV_PANEL || type == WRITEBACK_PANEL) {
Ravishangar Kalyanam898f4bd2011-07-15 18:25:47 -07003263#ifdef CONFIG_FB_MSM_HDMI_AS_PRIMARY
3264 pdata->panel_info.fb_num = 2;
3265#else
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003266 pdata->panel_info.fb_num = 1;
Ravishangar Kalyanam898f4bd2011-07-15 18:25:47 -07003267#endif
3268 }
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003269 else
3270 pdata->panel_info.fb_num = MSM_FB_NUM;
3271
3272 MSM_FB_INFO("setting pdata->panel_info.fb_num to %d. type: %d\n",
3273 pdata->panel_info.fb_num, type);
3274#endif
3275 fb_num = pdata->panel_info.fb_num;
3276
3277 if (fb_num <= 0)
3278 return NULL;
3279
3280 if (fbi_list_index >= MAX_FBI_LIST) {
3281 printk(KERN_ERR "msm_fb: no more framebuffer info list!\n");
3282 return NULL;
3283 }
3284 /*
3285 * alloc panel device data
3286 */
3287 this_dev = msm_fb_device_alloc(pdata, type, id);
3288
3289 if (!this_dev) {
3290 printk(KERN_ERR
3291 "%s: msm_fb_device_alloc failed!\n", __func__);
3292 return NULL;
3293 }
3294
3295 /*
3296 * alloc framebuffer info + par data
3297 */
3298 fbi = framebuffer_alloc(sizeof(struct msm_fb_data_type), NULL);
3299 if (fbi == NULL) {
3300 platform_device_put(this_dev);
3301 printk(KERN_ERR "msm_fb: can't alloca framebuffer info data!\n");
3302 return NULL;
3303 }
3304
3305 mfd = (struct msm_fb_data_type *)fbi->par;
3306 mfd->key = MFD_KEY;
3307 mfd->fbi = fbi;
3308 mfd->panel.type = type;
3309 mfd->panel.id = id;
3310 mfd->fb_page = fb_num;
3311 mfd->index = fbi_list_index;
3312 mfd->mdp_fb_page_protection = MDP_FB_PAGE_PROTECTION_WRITECOMBINE;
Nagamalleswararao Ganjie69bed82011-10-17 16:21:42 -07003313#ifdef CONFIG_MSM_MULTIMEDIA_USE_ION
3314 mfd->client = msm_ion_client_create(-1, pdev->name);
3315#endif
Bryan Huntsman3f2bc4d2011-08-16 17:27:22 -07003316 /* link to the latest pdev */
3317 mfd->pdev = this_dev;
3318
3319 mfd_list[mfd_list_index++] = mfd;
3320 fbi_list[fbi_list_index++] = fbi;
3321
3322 /*
3323 * set driver data
3324 */
3325 platform_set_drvdata(this_dev, mfd);
3326
3327 if (platform_device_add(this_dev)) {
3328 printk(KERN_ERR "msm_fb: platform_device_add failed!\n");
3329 platform_device_put(this_dev);
3330 framebuffer_release(fbi);
3331 fbi_list_index--;
3332 return NULL;
3333 }
3334 return this_dev;
3335}
3336EXPORT_SYMBOL(msm_fb_add_device);
3337
3338int get_fb_phys_info(unsigned long *start, unsigned long *len, int fb_num)
3339{
3340 struct fb_info *info;
3341
3342 if (fb_num > MAX_FBI_LIST)
3343 return -1;
3344
3345 info = fbi_list[fb_num];
3346 if (!info)
3347 return -1;
3348
3349 *start = info->fix.smem_start;
3350 *len = info->fix.smem_len;
3351 return 0;
3352}
3353EXPORT_SYMBOL(get_fb_phys_info);
3354
3355int __init msm_fb_init(void)
3356{
3357 int rc = -ENODEV;
3358
3359 if (msm_fb_register_driver())
3360 return rc;
3361
3362#ifdef MSM_FB_ENABLE_DBGFS
3363 {
3364 struct dentry *root;
3365
3366 if ((root = msm_fb_get_debugfs_root()) != NULL) {
3367 msm_fb_debugfs_file_create(root,
3368 "msm_fb_msg_printing_level",
3369 (u32 *) &msm_fb_msg_level);
3370 msm_fb_debugfs_file_create(root,
3371 "mddi_msg_printing_level",
3372 (u32 *) &mddi_msg_level);
3373 msm_fb_debugfs_file_create(root, "msm_fb_debug_enabled",
3374 (u32 *) &msm_fb_debug_enabled);
3375 }
3376 }
3377#endif
3378
3379 return 0;
3380}
3381
3382module_init(msm_fb_init);