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