/* Copyright (c) 2012, The Linux Foundation. All rights reserved. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "msm_fb.h" struct mdp_ccs mdp_ccs_rgb2yuv; struct mdp_ccs mdp_ccs_yuv2rgb; static struct platform_device *pdev_list[MSM_FB_MAX_DEV_LIST]; static int pdev_list_cnt; static int ebi2_host_resource_initialized; static struct msm_panel_common_pdata *ebi2_host_pdata; static int ebi2_host_probe(struct platform_device *pdev); static int ebi2_host_remove(struct platform_device *pdev); static int ebi2_host_runtime_suspend(struct device *dev) { dev_dbg(dev, "pm_runtime: suspending...\n"); return 0; } static int ebi2_host_runtime_resume(struct device *dev) { dev_dbg(dev, "pm_runtime: resuming...\n"); return 0; } static const struct dev_pm_ops ebi2_host_dev_pm_ops = { .runtime_suspend = ebi2_host_runtime_suspend, .runtime_resume = ebi2_host_runtime_resume, }; static struct platform_driver ebi2_host_driver = { .probe = ebi2_host_probe, .remove = ebi2_host_remove, .shutdown = NULL, .driver = { /* * Simulate mdp hw */ .name = "mdp", .pm = &ebi2_host_dev_pm_ops, }, }; void mdp_pipe_ctrl(MDP_BLOCK_TYPE block, MDP_BLOCK_POWER_STATE state, boolean isr) { return; } int mdp_ppp_blit(struct fb_info *info, struct mdp_blit_req *req) { return 0; } int mdp_start_histogram(struct fb_info *info) { return 0; } int mdp_stop_histogram(struct fb_info *info) { return 0; } void mdp_refresh_screen(unsigned long data) { return; } static int ebi2_host_off(struct platform_device *pdev) { int ret; ret = panel_next_off(pdev); return ret; } static int ebi2_host_on(struct platform_device *pdev) { int ret; ret = panel_next_on(pdev); return ret; } static int ebi2_host_probe(struct platform_device *pdev) { struct platform_device *msm_fb_dev = NULL; struct msm_fb_data_type *mfd; struct msm_fb_panel_data *pdata = NULL; int rc; if ((pdev->id == 0) && (pdev->num_resources > 0)) { ebi2_host_pdata = pdev->dev.platform_data; ebi2_host_resource_initialized = 1; return 0; } ebi2_host_resource_initialized = 1; if (!ebi2_host_resource_initialized) return -EPERM; mfd = platform_get_drvdata(pdev); if (!mfd) return -ENODEV; if (mfd->key != MFD_KEY) return -EINVAL; if (pdev_list_cnt >= MSM_FB_MAX_DEV_LIST) return -ENOMEM; msm_fb_dev = platform_device_alloc("msm_fb", pdev->id); if (!msm_fb_dev) return -ENOMEM; /* link to the latest pdev */ mfd->pdev = msm_fb_dev; if (ebi2_host_pdata) { mfd->mdp_rev = ebi2_host_pdata->mdp_rev; mfd->mem_hid = ebi2_host_pdata->mem_hid; } /* add panel data */ if (platform_device_add_data (msm_fb_dev, pdev->dev.platform_data, sizeof(struct msm_fb_panel_data))) { pr_err("ebi2_host_probe: platform_device_add_data failed!\n"); rc = -ENOMEM; goto ebi2_host_probe_err; } /* data chain */ pdata = msm_fb_dev->dev.platform_data; pdata->on = ebi2_host_on; pdata->off = ebi2_host_off; pdata->next = pdev; /* set driver data */ platform_set_drvdata(msm_fb_dev, mfd); rc = platform_device_add(msm_fb_dev); if (rc) goto ebi2_host_probe_err; pm_runtime_set_active(&pdev->dev); pm_runtime_enable(&pdev->dev); pdev_list[pdev_list_cnt++] = pdev; return 0; ebi2_host_probe_err: platform_device_put(msm_fb_dev); return rc; } void mdp_set_dma_pan_info(struct fb_info *info, struct mdp_dirty_region *dirty, boolean sync) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; struct fb_info *fbi = mfd->fbi; struct msm_panel_info *panel_info = &mfd->panel_info; MDPIBUF *iBuf; int bpp = info->var.bits_per_pixel / 8; int yres, remainder; if (panel_info->mode2_yres != 0) { yres = panel_info->mode2_yres; remainder = (fbi->fix.line_length*yres)%PAGE_SIZE; } else { yres = panel_info->yres; remainder = (fbi->fix.line_length*yres)%PAGE_SIZE; } if (!remainder) remainder = PAGE_SIZE; down(&mfd->sem); iBuf = &mfd->ibuf; /* use virtual address */ iBuf->buf = (uint8 *) fbi->screen_base; if (fbi->var.yoffset < yres) { iBuf->buf += fbi->var.xoffset * bpp; } else if (fbi->var.yoffset >= yres && fbi->var.yoffset < 2 * yres) { iBuf->buf += fbi->var.xoffset * bpp + yres * fbi->fix.line_length + PAGE_SIZE - remainder; } else { iBuf->buf += fbi->var.xoffset * bpp + 2 * yres * fbi->fix.line_length + 2 * (PAGE_SIZE - remainder); } iBuf->ibuf_width = info->var.xres_virtual; iBuf->bpp = bpp; iBuf->vsync_enable = sync; if (dirty) { /* * ToDo: dirty region check inside var.xoffset+xres * <-> var.yoffset+yres */ iBuf->dma_x = dirty->xoffset % info->var.xres; iBuf->dma_y = dirty->yoffset % info->var.yres; iBuf->dma_w = dirty->width; iBuf->dma_h = dirty->height; } else { iBuf->dma_x = 0; iBuf->dma_y = 0; iBuf->dma_w = info->var.xres; iBuf->dma_h = info->var.yres; } mfd->ibuf_flushed = FALSE; up(&mfd->sem); } void mdp_dma_pan_update(struct fb_info *info) { struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; MDPIBUF *iBuf; int i, j; uint32 data; uint8 *src; struct msm_fb_panel_data *pdata = (struct msm_fb_panel_data *)mfd->pdev->dev.platform_data; struct fb_info *fbi = mfd->fbi; iBuf = &mfd->ibuf; invalidate_caches((unsigned long)fbi->screen_base, (unsigned long)info->fix.smem_len, (unsigned long)info->fix.smem_start); pdata->set_rect(iBuf->dma_x, iBuf->dma_y, iBuf->dma_w, iBuf->dma_h); for (i = 0; i < iBuf->dma_h; i++) { src = iBuf->buf + (fbi->fix.line_length * (iBuf->dma_y + i)) + (iBuf->dma_x * iBuf->bpp); for (j = 0; j < iBuf->dma_w; j++) { data = (uint32)(*src++ >> 2) << 12; data |= (uint32)(*src++ >> 2) << 6; data |= (uint32)(*src++ >> 2); data = ((data&0x1FF)<<16) | ((data&0x3FE00)>>9); outpdw(mfd->data_port, data); } } } static int ebi2_host_remove(struct platform_device *pdev) { pm_runtime_disable(&pdev->dev); return 0; } static int ebi2_host_register_driver(void) { return platform_driver_register(&ebi2_host_driver); } static int __init ebi2_host_driver_init(void) { int ret; ret = ebi2_host_register_driver(); if (ret) { pr_err("ebi2_host_register_driver() failed!\n"); return ret; } return 0; } module_init(ebi2_host_driver_init);