diff --git a/drivers/video/msm/Kconfig b/drivers/video/msm/Kconfig index 777715454d4..4e1c8074a85 100644 --- a/drivers/video/msm/Kconfig +++ b/drivers/video/msm/Kconfig @@ -84,6 +84,16 @@ config FB_MSM_MDP40 Support for MSM MDP HW revision 4.0 Say Y here if this is msm7x30 variant platform. +config FB_MSM_MDSS + bool "MDSS HW" + ---help--- + The Mobile Display Sub System (MDSS) driver supports devices which + contain MDSS hardware block. + + The MDSS driver implements frame buffer interface to provide access to + the display hardware and provide a way for users to display graphics + on connected display panels. + config FB_MSM_MDP_NONE bool "MDP HW None" ---help--- @@ -936,4 +946,5 @@ config FB_MSM_EBI2_PANEL_DETECT default n ---help--- Support for EBI2 panel auto detect + endif diff --git a/drivers/video/msm/Makefile b/drivers/video/msm/Makefile index e4a09483759..a0f9e02ddb4 100644 --- a/drivers/video/msm/Makefile +++ b/drivers/video/msm/Makefile @@ -1,10 +1,12 @@ +ifeq ($(CONFIG_FB_MSM_MDSS),y) +obj-y += mdss/ +else obj-y := msm_fb.o obj-$(CONFIG_FB_MSM_LOGO) += logo.o obj-$(CONFIG_FB_BACKLIGHT) += msm_fb_bl.o ifeq ($(CONFIG_FB_MSM_MDP_HW),y) - # MDP obj-y += mdp.o @@ -182,15 +184,15 @@ obj-$(CONFIG_FB_MSM_EXTMDDI_SVGA) += mddi_ext_lcd.o obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_wfd_writeback_panel.o obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_wfd_writeback.o obj-$(CONFIG_FB_MSM_WRITEBACK_MSM_PANEL) += mdp4_overlay_writeback.o - -obj-$(CONFIG_MSM_VIDC_1080P) += vidc/ -obj-$(CONFIG_MSM_VIDC_720P) += vidc/ else obj-$(CONFIG_FB_MSM_EBI2) += ebi2_host.o obj-$(CONFIG_FB_MSM_EBI2) += ebi2_lcd.o obj-y += msm_fb_panel.o obj-$(CONFIG_FB_MSM_EBI2_EPSON_S1D_QVGA_PANEL) += ebi2_epson_s1d_qvga.o endif +endif +obj-$(CONFIG_MSM_VIDC_1080P) += vidc/ +obj-$(CONFIG_MSM_VIDC_720P) += vidc/ clean: rm *.o .*cmd diff --git a/drivers/video/msm/mdss/Makefile b/drivers/video/msm/mdss/Makefile new file mode 100644 index 00000000000..fa4843c53c1 --- /dev/null +++ b/drivers/video/msm/mdss/Makefile @@ -0,0 +1,4 @@ +mdss-mdp-objs := mdss_mdp.o mdss_mdp_ctl.o mdss_mdp_pipe.o mdss_mdp_util.o +mdss-mdp-objs += mdss_mdp_overlay.o +obj-$(CONFIG_FB_MSM_MDSS) += mdss-mdp.o +obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o diff --git a/drivers/video/msm/mdss/mdss.h b/drivers/video/msm/mdss/mdss.h new file mode 100644 index 00000000000..aaf66901047 --- /dev/null +++ b/drivers/video/msm/mdss/mdss.h @@ -0,0 +1,63 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef MDSS_H +#define MDSS_H + +#include +#include +#include + +#define MDSS_REG_WRITE(addr, val) writel_relaxed(val, mdss_reg_base + addr) +#define MDSS_REG_READ(addr) readl_relaxed(mdss_reg_base + addr) + +extern unsigned char *mdss_reg_base; + +struct mdss_res_type { + u32 rev; + u32 mdp_rev; + struct clk *mdp_clk; + struct clk *mdp_pclk; + struct clk *mdp_lut_clk; + struct clk *vsync_clk; + struct regulator *fs; + + struct workqueue_struct *clk_ctrl_wq; + struct delayed_work clk_ctrl_worker; + + u32 irq; + u32 irq_mask; + u32 irq_ena; + u32 irq_buzy; + + u32 clk_ena; + u32 suspend; + u32 timeout; + + u32 fs_ena; + u32 vsync_ena; + + u32 intf; + u32 eintf_ena; + u32 prim_ptype; + u32 res_init; + u32 pdev_lcnt; + u32 bus_hdl; + + u32 smp_mb_cnt; + u32 smp_mb_size; + u32 *pipe_type_map; + u32 *mixer_type_map; +}; +extern struct mdss_res_type *mdss_res; +#endif /* MDSS_H */ diff --git a/drivers/video/msm/mdss/mdss_fb.c b/drivers/video/msm/mdss/mdss_fb.c new file mode 100644 index 00000000000..cec9cb8b2b1 --- /dev/null +++ b/drivers/video/msm/mdss/mdss_fb.c @@ -0,0 +1,1228 @@ +/* + * Core MDSS framebuffer driver. + * + * Copyright (C) 2007 Google Incorporated + * Copyright (c) 2008-2012, Code Aurora Forum. 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "mdss_fb.h" +#include "mdss_mdp.h" + +#ifdef CONFIG_FB_MSM_TRIPLE_BUFFER +#define MDSS_FB_NUM 3 +#else +#define MDSS_FB_NUM 2 +#endif + +#define MAX_FBI_LIST 32 +static struct fb_info *fbi_list[MAX_FBI_LIST]; +static int fbi_list_index; + +static u32 mdss_fb_pseudo_palette[16] = { + 0x00000000, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, + 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff +}; + +static int mdss_fb_register(struct msm_fb_data_type *mfd); +static int mdss_fb_open(struct fb_info *info, int user); +static int mdss_fb_release(struct fb_info *info, int user); +static int mdss_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info); +static int mdss_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info); +static int mdss_fb_set_par(struct fb_info *info); +static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, + int op_enable); +static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd); +static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg); +static int mdss_fb_mmap(struct fb_info *info, struct vm_area_struct *vma); + +#define MAX_BACKLIGHT_BRIGHTNESS 255 +static int lcd_backlight_registered; + +static void mdss_fb_set_bl_brightness(struct led_classdev *led_cdev, + enum led_brightness value) +{ + struct msm_fb_data_type *mfd = dev_get_drvdata(led_cdev->dev->parent); + int bl_lvl; + + if (value > MAX_BACKLIGHT_BRIGHTNESS) + value = MAX_BACKLIGHT_BRIGHTNESS; + + /* This maps android backlight level 0 to 255 into + driver backlight level 0 to bl_max with rounding */ + bl_lvl = (2 * value * mfd->panel_info.bl_max + MAX_BACKLIGHT_BRIGHTNESS) + /(2 * MAX_BACKLIGHT_BRIGHTNESS); + + if (!bl_lvl && value) + bl_lvl = 1; + + mdss_fb_set_backlight(mfd, bl_lvl); +} + +static struct led_classdev backlight_led = { + .name = "lcd-backlight", + .brightness = MAX_BACKLIGHT_BRIGHTNESS, + .brightness_set = mdss_fb_set_bl_brightness, +}; + +static ssize_t mdss_fb_get_type(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + + switch (mfd->panel_info.type) { + case NO_PANEL: + ret = snprintf(buf, PAGE_SIZE, "no panel\n"); + break; + case HDMI_PANEL: + ret = snprintf(buf, PAGE_SIZE, "hdmi panel\n"); + break; + case LVDS_PANEL: + ret = snprintf(buf, PAGE_SIZE, "lvds panel\n"); + break; + case DTV_PANEL: + ret = snprintf(buf, PAGE_SIZE, "dtv panel\n"); + break; + case MIPI_VIDEO_PANEL: + ret = snprintf(buf, PAGE_SIZE, "mipi dsi video panel\n"); + break; + case MIPI_CMD_PANEL: + ret = snprintf(buf, PAGE_SIZE, "mipi dsi cmd panel\n"); + break; + case WRITEBACK_PANEL: + ret = snprintf(buf, PAGE_SIZE, "writeback panel\n"); + break; + default: + ret = snprintf(buf, PAGE_SIZE, "unknown panel\n"); + break; + } + + return ret; +} + +static DEVICE_ATTR(mdss_fb_type, S_IRUGO, mdss_fb_get_type, NULL); +static struct attribute *mdss_fb_attrs[] = { + &dev_attr_mdss_fb_type.attr, + NULL, +}; + +static struct attribute_group mdss_fb_attr_group = { + .attrs = mdss_fb_attrs, +}; + +static int mdss_fb_create_sysfs(struct msm_fb_data_type *mfd) +{ + int rc; + + rc = sysfs_create_group(&mfd->fbi->dev->kobj, &mdss_fb_attr_group); + if (rc) + pr_err("sysfs group creation failed, rc=%d\n", rc); + return rc; +} + +static void mdss_fb_remove_sysfs(struct msm_fb_data_type *mfd) +{ + sysfs_remove_group(&mfd->fbi->dev->kobj, &mdss_fb_attr_group); +} + +static int mdss_fb_probe(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd = NULL; + struct mdss_panel_data *pdata; + struct fb_info *fbi; + int rc; + + if (fbi_list_index >= MAX_FBI_LIST) + return -ENOMEM; + + pdata = dev_get_platdata(&pdev->dev); + if (!pdata) + return -ENODEV; + + /* + * alloc framebuffer info + par data + */ + fbi = framebuffer_alloc(sizeof(struct msm_fb_data_type), &pdev->dev); + if (fbi == NULL) { + pr_err("can't allocate framebuffer info data!\n"); + return -ENOMEM; + } + + mfd = (struct msm_fb_data_type *)fbi->par; + mfd->key = MFD_KEY; + mfd->fbi = fbi; + mfd->panel_info = pdata->panel_info; + mfd->panel.type = pdata->panel_info.type; + mfd->panel.id = mfd->index; + mfd->fb_page = MDSS_FB_NUM; + mfd->index = fbi_list_index; + mfd->mdp_fb_page_protection = MDP_FB_PAGE_PROTECTION_WRITECOMBINE; + mfd->panel_info.frame_count = 0; + mfd->bl_level = 0; + mfd->fb_imgType = MDP_RGBA_8888; + mfd->iclient = msm_ion_client_create(-1, pdev->name); + if (IS_ERR(mfd->iclient)) + mfd->iclient = NULL; + + mfd->pdev = pdev; + + mutex_init(&mfd->lock); + + fbi_list[fbi_list_index++] = fbi; + + platform_set_drvdata(pdev, mfd); + + rc = mdss_fb_register(mfd); + if (rc) + return rc; + + rc = pm_runtime_set_active(mfd->fbi->dev); + if (rc < 0) + pr_err("pm_runtime: fail to set active.\n"); + pm_runtime_enable(mfd->fbi->dev); + + /* android supports only one lcd-backlight/lcd for now */ + if (!lcd_backlight_registered) { + if (led_classdev_register(&pdev->dev, &backlight_led)) + pr_err("led_classdev_register failed\n"); + else + lcd_backlight_registered = 1; + } + + mdss_fb_create_sysfs(mfd); + + return 0; +} + +static int mdss_fb_remove(struct platform_device *pdev) +{ + struct msm_fb_data_type *mfd; + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + mdss_fb_remove_sysfs(mfd); + + pm_runtime_disable(mfd->fbi->dev); + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (mdss_fb_suspend_sub(mfd)) + pr_err("msm_fb_remove: can't stop the device %d\n", + mfd->index); + + /* remove /dev/fb* */ + unregister_framebuffer(mfd->fbi); + + if (lcd_backlight_registered) { + lcd_backlight_registered = 0; + led_classdev_unregister(&backlight_led); + } + + return 0; +} + +static int mdss_fb_suspend_sub(struct msm_fb_data_type *mfd) +{ + int ret = 0; + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + /* + * suspend this channel + */ + mfd->suspend.op_enable = mfd->op_enable; + mfd->suspend.panel_power_on = mfd->panel_power_on; + + if (mfd->op_enable) { + ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, mfd->fbi, + mfd->suspend.op_enable); + if (ret) { + pr_warn("can't turn off display!\n"); + return ret; + } + mfd->op_enable = false; + } + + return 0; +} + +#if defined(CONFIG_PM) +static int mdss_fb_resume_sub(struct msm_fb_data_type *mfd) +{ + int ret = 0; + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + /* resume state var recover */ + mfd->op_enable = mfd->suspend.op_enable; + + if (mfd->suspend.panel_power_on) { + ret = mdss_fb_blank_sub(FB_BLANK_UNBLANK, mfd->fbi, + mfd->op_enable); + if (ret) + pr_warn("can't turn on display!\n"); + } + + return ret; +} + +static int mdss_fb_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct msm_fb_data_type *mfd; + int ret = 0; + + pr_debug("mdss_fb_suspend\n"); + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + console_lock(); + fb_set_suspend(mfd->fbi, FBINFO_STATE_SUSPENDED); + + ret = mdss_fb_suspend_sub(mfd); + if (ret != 0) { + pr_err("failed to suspend! %d\n", ret); + fb_set_suspend(mfd->fbi, FBINFO_STATE_RUNNING); + } else { + pdev->dev.power.power_state = state; + } + + console_unlock(); + return ret; +} + +static int mdss_fb_resume(struct platform_device *pdev) +{ + /* This resume function is called when interrupt is enabled. + */ + int ret = 0; + struct msm_fb_data_type *mfd; + + pr_debug("mdss_fb_resume\n"); + + mfd = (struct msm_fb_data_type *)platform_get_drvdata(pdev); + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + console_lock(); + ret = mdss_fb_resume_sub(mfd); + pdev->dev.power.power_state = PMSG_ON; + fb_set_suspend(mfd->fbi, FBINFO_STATE_RUNNING); + console_unlock(); + + return ret; +} +#else +#define mdss_fb_suspend NULL +#define mdss_fb_resume NULL +#endif + +#if defined(CONFIG_PM) && defined(CONFIG_SUSPEND) +static int mdss_fb_ext_suspend(struct device *dev) +{ + struct msm_fb_data_type *mfd = dev_get_drvdata(dev); + int ret = 0; + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + if (mfd->panel_info.type == HDMI_PANEL || + mfd->panel_info.type == DTV_PANEL) + ret = mdss_fb_suspend_sub(mfd); + + return ret; +} + +static int mdss_fb_ext_resume(struct device *dev) +{ + struct msm_fb_data_type *mfd = dev_get_drvdata(dev); + int ret = 0; + + if ((!mfd) || (mfd->key != MFD_KEY)) + return 0; + + if (mfd->panel_info.type == HDMI_PANEL || + mfd->panel_info.type == DTV_PANEL) + ret = mdss_fb_resume_sub(mfd); + + return ret; +} +#else +#define mdss_fb_ext_suspend NULL +#define mdss_fb_ext_resume NULL +#endif + +static const struct dev_pm_ops mdss_fb_dev_pm_ops = { + .suspend = mdss_fb_ext_suspend, + .resume = mdss_fb_ext_resume, +}; + +static struct platform_driver mdss_fb_driver = { + .probe = mdss_fb_probe, + .remove = mdss_fb_remove, + .suspend = mdss_fb_suspend, + .resume = mdss_fb_resume, + .shutdown = NULL, + .driver = { + .name = "mdss_fb", + .pm = &mdss_fb_dev_pm_ops, + }, +}; + +static int unset_bl_level, bl_updated; +static int bl_level_old; + +void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl) +{ + struct mdss_panel_data *pdata; + + if (!mfd->panel_power_on || !bl_updated) { + unset_bl_level = bkl_lvl; + return; + } else { + unset_bl_level = 0; + } + + pdata = dev_get_platdata(&mfd->pdev->dev); + + if ((pdata) && (pdata->set_backlight)) { + mutex_lock(&mfd->lock); + if (bl_level_old == bkl_lvl) { + mutex_unlock(&mfd->lock); + return; + } + mfd->bl_level = bkl_lvl; + pdata->set_backlight(mfd->bl_level); + bl_level_old = mfd->bl_level; + mutex_unlock(&mfd->lock); + } +} + +void mdss_fb_update_backlight(struct msm_fb_data_type *mfd) +{ + struct mdss_panel_data *pdata; + + if (unset_bl_level && !bl_updated) { + pdata = dev_get_platdata(&mfd->pdev->dev); + if ((pdata) && (pdata->set_backlight)) { + mutex_lock(&mfd->lock); + mfd->bl_level = unset_bl_level; + pdata->set_backlight(mfd->bl_level); + bl_level_old = unset_bl_level; + mutex_unlock(&mfd->lock); + bl_updated = 1; + } + } +} + +static int mdss_fb_blank_sub(int blank_mode, struct fb_info *info, + int op_enable) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int ret = 0; + + if (!op_enable) + return -EPERM; + + switch (blank_mode) { + case FB_BLANK_UNBLANK: + if (!mfd->panel_power_on) { + msleep(20); + ret = mfd->on_fnc(mfd); + if (ret == 0) + mfd->panel_power_on = true; + } + break; + + case FB_BLANK_VSYNC_SUSPEND: + case FB_BLANK_HSYNC_SUSPEND: + case FB_BLANK_NORMAL: + case FB_BLANK_POWERDOWN: + default: + if (mfd->panel_power_on) { + int curr_pwr_state; + + mfd->op_enable = false; + curr_pwr_state = mfd->panel_power_on; + mfd->panel_power_on = false; + bl_updated = 0; + + msleep(20); + ret = mfd->off_fnc(mfd); + if (ret) + mfd->panel_power_on = curr_pwr_state; + + mfd->op_enable = true; + } + break; + } + + return ret; +} + +static int mdss_fb_blank(int blank_mode, struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + return mdss_fb_blank_sub(blank_mode, info, mfd->op_enable); +} + +/* + * Custom Framebuffer mmap() function for MSM driver. + * Differs from standard mmap() function by allowing for customized + * page-protection. + */ +static int mdss_fb_mmap(struct fb_info *info, struct vm_area_struct *vma) +{ + /* Get frame buffer memory range. */ + unsigned long start = info->fix.smem_start; + u32 len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.smem_len); + unsigned long off = vma->vm_pgoff << PAGE_SHIFT; + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if (off >= len) { + /* memory mapped io */ + off -= len; + if (info->var.accel_flags) { + mutex_unlock(&info->lock); + return -EINVAL; + } + start = info->fix.mmio_start; + len = PAGE_ALIGN((start & ~PAGE_MASK) + info->fix.mmio_len); + } + + /* Set VM flags. */ + start &= PAGE_MASK; + if ((vma->vm_end - vma->vm_start + off) > len) + return -EINVAL; + off += start; + vma->vm_pgoff = off >> PAGE_SHIFT; + /* This is an IO map - tell maydump to skip this VMA */ + vma->vm_flags |= VM_IO | VM_RESERVED; + + /* Set VM page protection */ + if (mfd->mdp_fb_page_protection == MDP_FB_PAGE_PROTECTION_WRITECOMBINE) + vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot); + else if (mfd->mdp_fb_page_protection == + MDP_FB_PAGE_PROTECTION_WRITETHROUGHCACHE) + vma->vm_page_prot = pgprot_writethroughcache(vma->vm_page_prot); + else if (mfd->mdp_fb_page_protection == + MDP_FB_PAGE_PROTECTION_WRITEBACKCACHE) + vma->vm_page_prot = pgprot_writebackcache(vma->vm_page_prot); + else if (mfd->mdp_fb_page_protection == + MDP_FB_PAGE_PROTECTION_WRITEBACKWACACHE) + vma->vm_page_prot = pgprot_writebackwacache(vma->vm_page_prot); + else + vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); + + /* Remap the frame buffer I/O range */ + if (io_remap_pfn_range(vma, vma->vm_start, off >> PAGE_SHIFT, + vma->vm_end - vma->vm_start, + vma->vm_page_prot)) + return -EAGAIN; + + return 0; +} + +static struct fb_ops mdss_fb_ops = { + .owner = THIS_MODULE, + .fb_open = mdss_fb_open, + .fb_release = mdss_fb_release, + .fb_check_var = mdss_fb_check_var, /* vinfo check */ + .fb_set_par = mdss_fb_set_par, /* set the video mode */ + .fb_blank = mdss_fb_blank, /* blank display */ + .fb_pan_display = mdss_fb_pan_display, /* pan display */ + .fb_ioctl = mdss_fb_ioctl, /* perform fb specific ioctl */ + .fb_mmap = mdss_fb_mmap, +}; + +static u32 mdss_fb_line_length(u32 fb_index, u32 xres, int bpp) +{ + /* The adreno GPU hardware requires that the pitch be aligned to + 32 pixels for color buffers, so for the cases where the GPU + is writing directly to fb0, the framebuffer pitch + also needs to be 32 pixel aligned */ + + if (fb_index == 0) + return ALIGN(xres, 32) * bpp; + else + return xres * bpp; +} + +static int mdss_fb_alloc_fbmem(struct msm_fb_data_type *mfd) +{ + void *virt = NULL; + unsigned long phys = 0; + size_t size; + + size = PAGE_ALIGN(mfd->fbi->fix.line_length * mfd->panel_info.yres); + size *= mfd->fb_page; + + if (mfd->index == 0) { + virt = dma_alloc_coherent(NULL, size, (dma_addr_t *) &phys, + GFP_KERNEL); + if (!virt) { + pr_err("unable to alloc fb memory size=%u\n", size); + return -ENOMEM; + } + + pr_info("allocating %u bytes at %p (%lx phys) for fb %d\n", + size, virt, phys, mfd->index); + } else { + pr_debug("no memory allocated for fb%d\n", mfd->index); + size = 0; + } + + mfd->fbi->screen_base = virt; + mfd->fbi->fix.smem_start = phys; + mfd->fbi->fix.smem_len = size; + + return 0; +} + +static int mdss_fb_register(struct msm_fb_data_type *mfd) +{ + int ret = -ENODEV; + int bpp; + struct mdss_panel_info *panel_info = &mfd->panel_info; + struct fb_info *fbi = mfd->fbi; + struct fb_fix_screeninfo *fix; + struct fb_var_screeninfo *var; + int *id; + + /* + * fb info initialization + */ + fix = &fbi->fix; + var = &fbi->var; + + fix->type_aux = 0; /* if type == FB_TYPE_INTERLEAVED_PLANES */ + fix->visual = FB_VISUAL_TRUECOLOR; /* True Color */ + fix->ywrapstep = 0; /* No support */ + fix->mmio_start = 0; /* No MMIO Address */ + fix->mmio_len = 0; /* No MMIO Address */ + fix->accel = FB_ACCEL_NONE;/* FB_ACCEL_MSM needes to be added in fb.h */ + + var->xoffset = 0, /* Offset from virtual to visible */ + var->yoffset = 0, /* resolution */ + var->grayscale = 0, /* No graylevels */ + var->nonstd = 0, /* standard pixel format */ + var->activate = FB_ACTIVATE_VBL, /* activate it at vsync */ + var->height = -1, /* height of picture in mm */ + var->width = -1, /* width of picture in mm */ + var->accel_flags = 0, /* acceleration flags */ + var->sync = 0, /* see FB_SYNC_* */ + var->rotate = 0, /* angle we rotate counter clockwise */ + mfd->op_enable = false; + + switch (mfd->fb_imgType) { + case MDP_RGB_565: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 0; + var->green.offset = 5; + var->red.offset = 11; + var->blue.length = 5; + var->green.length = 6; + var->red.length = 5; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 0; + bpp = 2; + break; + + case MDP_RGB_888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 0; + var->green.offset = 8; + var->red.offset = 16; + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 0; + bpp = 3; + break; + + case MDP_ARGB_8888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 0; + var->green.offset = 8; + var->red.offset = 16; + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 24; + var->transp.length = 8; + bpp = 4; + break; + + case MDP_RGBA_8888: + fix->type = FB_TYPE_PACKED_PIXELS; + fix->xpanstep = 1; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + var->blue.offset = 8; + var->green.offset = 16; + var->red.offset = 24; + var->blue.length = 8; + var->green.length = 8; + var->red.length = 8; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 8; + bpp = 4; + break; + + case MDP_YCRYCB_H2V1: + fix->type = FB_TYPE_INTERLEAVED_PLANES; + fix->xpanstep = 2; + fix->ypanstep = 1; + var->vmode = FB_VMODE_NONINTERLACED; + + /* how about R/G/B offset? */ + var->blue.offset = 0; + var->green.offset = 5; + var->red.offset = 11; + var->blue.length = 5; + var->green.length = 6; + var->red.length = 5; + var->blue.msb_right = 0; + var->green.msb_right = 0; + var->red.msb_right = 0; + var->transp.offset = 0; + var->transp.length = 0; + bpp = 2; + break; + + default: + pr_err("msm_fb_init: fb %d unkown image type!\n", + mfd->index); + return ret; + } + + fix->type = panel_info->is_3d_panel; + fix->line_length = mdss_fb_line_length(mfd->index, panel_info->xres, + bpp); + mfd->var_xres = panel_info->xres; + mfd->var_yres = panel_info->yres; + + var->pixclock = mfd->panel_info.clk_rate; + mfd->var_pixclock = var->pixclock; + + var->xres = panel_info->xres; + var->yres = panel_info->yres; + var->xres_virtual = panel_info->xres; + var->yres_virtual = panel_info->yres * mfd->fb_page; + var->bits_per_pixel = bpp * 8; /* FrameBuffer color depth */ + + /* id field for fb app */ + + id = (int *)&mfd->panel; + + snprintf(fix->id, sizeof(fix->id), "mdssfb_%x", (u32) *id); + + fbi->fbops = &mdss_fb_ops; + fbi->flags = FBINFO_FLAG_DEFAULT; + fbi->pseudo_palette = mdss_fb_pseudo_palette; + + mfd->ref_cnt = 0; + mfd->panel_power_on = false; + + if (mdss_fb_alloc_fbmem(mfd)) { + pr_err("unable to allocate framebuffer memory\n"); + return -ENOMEM; + } + + mfd->op_enable = true; + + /* cursor memory allocation */ + if (mfd->cursor_update) { + mfd->cursor_buf = dma_alloc_coherent(NULL, MDSS_MDP_CURSOR_SIZE, + (dma_addr_t *) &mfd->cursor_buf_phys, + GFP_KERNEL); + if (!mfd->cursor_buf) + mfd->cursor_update = 0; + } + + if (mfd->lut_update) { + ret = fb_alloc_cmap(&fbi->cmap, 256, 0); + if (ret) + pr_err("fb_alloc_cmap() failed!\n"); + } + + if (register_framebuffer(fbi) < 0) { + if (mfd->lut_update) + fb_dealloc_cmap(&fbi->cmap); + + if (mfd->cursor_buf) + dma_free_coherent(NULL, MDSS_MDP_CURSOR_SIZE, + mfd->cursor_buf, + (dma_addr_t) mfd->cursor_buf_phys); + + mfd->op_enable = false; + return -EPERM; + } + + pr_info("FrameBuffer[%d] %dx%d size=%d registered successfully!\n", + mfd->index, fbi->var.xres, fbi->var.yres, + fbi->fix.smem_len); + + ret = 0; + + return ret; +} + +static int mdss_fb_open(struct fb_info *info, int user) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int result; + + result = pm_runtime_get_sync(info->dev); + + if (result < 0) + pr_err("pm_runtime: fail to wake up\n"); + + + if (!mfd->ref_cnt) { + result = mdss_fb_blank_sub(FB_BLANK_UNBLANK, info, + mfd->op_enable); + if (result) { + pr_err("mdss_fb_open: can't turn on display!\n"); + return result; + } + } + + mfd->ref_cnt++; + return 0; +} + +static int mdss_fb_release(struct fb_info *info, int user) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + int ret = 0; + + if (!mfd->ref_cnt) { + pr_info("try to close unopened fb %d!\n", mfd->index); + return -EINVAL; + } + + mfd->ref_cnt--; + + if (!mfd->ref_cnt) { + ret = mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info, + mfd->op_enable); + if (ret) { + pr_err("can't turn off display!\n"); + return ret; + } + } + + pm_runtime_put(info->dev); + return ret; +} + +static int mdss_fb_pan_display(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + + if ((!mfd->op_enable) || (!mfd->panel_power_on)) + return -EPERM; + + if (var->xoffset > (info->var.xres_virtual - info->var.xres)) + return -EINVAL; + + if (var->yoffset > (info->var.yres_virtual - info->var.yres)) + return -EINVAL; + + if (info->fix.xpanstep) + info->var.xoffset = + (var->xoffset / info->fix.xpanstep) * info->fix.xpanstep; + + if (info->fix.ypanstep) + info->var.yoffset = + (var->yoffset / info->fix.ypanstep) * info->fix.ypanstep; + + if (mfd->dma_fnc) + mfd->dma_fnc(mfd); + else + pr_warn("dma function not set for panel type=%d\n", + mfd->panel.type); + + mdss_fb_update_backlight(mfd); + + ++mfd->panel_info.frame_count; + return 0; +} + +static int mdss_fb_check_var(struct fb_var_screeninfo *var, + struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + u32 len; + + if (var->rotate != FB_ROTATE_UR) + return -EINVAL; + if (var->grayscale != info->var.grayscale) + return -EINVAL; + + switch (var->bits_per_pixel) { + case 16: + if ((var->green.offset != 5) || + !((var->blue.offset == 11) + || (var->blue.offset == 0)) || + !((var->red.offset == 11) + || (var->red.offset == 0)) || + (var->blue.length != 5) || + (var->green.length != 6) || + (var->red.length != 5) || + (var->blue.msb_right != 0) || + (var->green.msb_right != 0) || + (var->red.msb_right != 0) || + (var->transp.offset != 0) || + (var->transp.length != 0)) + return -EINVAL; + break; + + case 24: + if ((var->blue.offset != 0) || + (var->green.offset != 8) || + (var->red.offset != 16) || + (var->blue.length != 8) || + (var->green.length != 8) || + (var->red.length != 8) || + (var->blue.msb_right != 0) || + (var->green.msb_right != 0) || + (var->red.msb_right != 0) || + !(((var->transp.offset == 0) && + (var->transp.length == 0)) || + ((var->transp.offset == 24) && + (var->transp.length == 8)))) + return -EINVAL; + break; + + case 32: + /* Figure out if the user meant RGBA or ARGB + and verify the position of the RGB components */ + + if (var->transp.offset == 24) { + if ((var->blue.offset != 0) || + (var->green.offset != 8) || + (var->red.offset != 16)) + return -EINVAL; + } else if (var->transp.offset == 0) { + if ((var->blue.offset != 8) || + (var->green.offset != 16) || + (var->red.offset != 24)) + return -EINVAL; + } else + return -EINVAL; + + /* Check the common values for both RGBA and ARGB */ + + if ((var->blue.length != 8) || + (var->green.length != 8) || + (var->red.length != 8) || + (var->transp.length != 8) || + (var->blue.msb_right != 0) || + (var->green.msb_right != 0) || + (var->red.msb_right != 0)) + return -EINVAL; + + break; + + default: + return -EINVAL; + } + + if ((var->xres_virtual <= 0) || (var->yres_virtual <= 0)) + return -EINVAL; + + len = var->xres_virtual * var->yres_virtual * (var->bits_per_pixel / 8); + if (len > info->fix.smem_len) + return -EINVAL; + + if ((var->xres == 0) || (var->yres == 0)) + return -EINVAL; + + if ((var->xres > mfd->panel_info.xres) || + (var->yres > mfd->panel_info.yres)) + return -EINVAL; + + if (var->xoffset > (var->xres_virtual - var->xres)) + return -EINVAL; + + if (var->yoffset > (var->yres_virtual - var->yres)) + return -EINVAL; + + return 0; +} + +static int mdss_fb_set_par(struct fb_info *info) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct fb_var_screeninfo *var = &info->var; + int old_imgType; + int blank = 0; + + old_imgType = mfd->fb_imgType; + switch (var->bits_per_pixel) { + case 16: + if (var->red.offset == 0) + mfd->fb_imgType = MDP_BGR_565; + else + mfd->fb_imgType = MDP_RGB_565; + break; + + case 24: + if ((var->transp.offset == 0) && (var->transp.length == 0)) + mfd->fb_imgType = MDP_RGB_888; + else if ((var->transp.offset == 24) && + (var->transp.length == 8)) { + mfd->fb_imgType = MDP_ARGB_8888; + info->var.bits_per_pixel = 32; + } + break; + + case 32: + if (var->transp.offset == 24) + mfd->fb_imgType = MDP_ARGB_8888; + else + mfd->fb_imgType = MDP_RGBA_8888; + break; + + default: + return -EINVAL; + } + + if ((mfd->var_pixclock != var->pixclock) || + (mfd->hw_refresh && ((mfd->fb_imgType != old_imgType) || + (mfd->var_pixclock != var->pixclock) || + (mfd->var_xres != var->xres) || + (mfd->var_yres != var->yres)))) { + mfd->var_xres = var->xres; + mfd->var_yres = var->yres; + mfd->var_pixclock = var->pixclock; + blank = 1; + } + mfd->fbi->fix.line_length = mdss_fb_line_length(mfd->index, var->xres, + var->bits_per_pixel / 8); + + if (blank) { + mdss_fb_blank_sub(FB_BLANK_POWERDOWN, info, mfd->op_enable); + mdss_fb_blank_sub(FB_BLANK_UNBLANK, info, mfd->op_enable); + } + + return 0; +} + +static int mdss_fb_cursor(struct fb_info *info, void __user *p) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct fb_cursor cursor; + int ret; + + if (!mfd->cursor_update) + return -ENODEV; + + ret = copy_from_user(&cursor, p, sizeof(cursor)); + if (ret) + return ret; + + return mfd->cursor_update(info, &cursor); +} + +static int mdss_fb_set_lut(struct fb_info *info, void __user *p) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct fb_cmap cmap; + int ret; + + if (!mfd->lut_update) + return -ENODEV; + + ret = copy_from_user(&cmap, p, sizeof(cmap)); + if (ret) + return ret; + + mfd->lut_update(info, &cmap); + return 0; +} + +static int mdss_fb_ioctl(struct fb_info *info, unsigned int cmd, + unsigned long arg) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + void __user *argp = (void __user *)arg; + struct mdp_page_protection fb_page_protection; + int ret = 0; + + switch (cmd) { + case MSMFB_CURSOR: + ret = mdss_fb_cursor(info, argp); + break; + + case MSMFB_SET_LUT: + ret = mdss_fb_set_lut(info, argp); + break; + + case MSMFB_GET_PAGE_PROTECTION: + fb_page_protection.page_protection = + mfd->mdp_fb_page_protection; + ret = copy_to_user(argp, &fb_page_protection, + sizeof(fb_page_protection)); + if (ret) + return ret; + break; + + default: + pr_info("MDP: unknown ioctl (cmd=%x) received!\n", cmd); + ret = -ENOSYS; + break; + } + + return ret; +} + +int mdss_register_panel(struct mdss_panel_data *pdata) +{ + struct platform_device *mdss_fb_dev = NULL; + struct msm_fb_data_type *mfd; + int rc; + + if (!mdss_res) { + pr_err("mdss mdp resources not initialized yet\n"); + return -ENODEV; + } + + mdss_fb_dev = platform_device_alloc("mdss_fb", pdata->panel_info.pdest); + if (!mdss_fb_dev) { + pr_err("unable to allocate mdss_fb device\n"); + return -ENOMEM; + } + + mdss_fb_dev->dev.platform_data = pdata; + + rc = platform_device_add(mdss_fb_dev); + if (rc) { + platform_device_put(mdss_fb_dev); + pr_err("unable to probe mdss_fb device (%d)\n", rc); + return rc; + } + + mfd = platform_get_drvdata(mdss_fb_dev); + if (!mfd) + return -ENODEV; + if (mfd->key != MFD_KEY) + return -EINVAL; + + mfd->on_fnc = mdss_mdp_ctl_on; + mfd->off_fnc = mdss_mdp_ctl_off; + + rc = mdss_mdp_overlay_init(mfd); + if (rc) + pr_err("unable to init overlay\n"); + + return rc; +} +EXPORT_SYMBOL(mdss_register_panel); + +int mdss_fb_get_phys_info(unsigned long *start, unsigned long *len, int fb_num) +{ + struct fb_info *info; + + if (fb_num > MAX_FBI_LIST) + return -EINVAL; + + info = fbi_list[fb_num]; + if (!info) + return -ENOENT; + + *start = info->fix.smem_start; + *len = info->fix.smem_len; + return 0; +} +EXPORT_SYMBOL(mdss_fb_get_phys_info); + +int __init mdss_fb_init(void) +{ + int rc = -ENODEV; + + if (platform_driver_register(&mdss_fb_driver)) + return rc; + + return 0; +} + +module_init(mdss_fb_init); diff --git a/drivers/video/msm/mdss/mdss_fb.h b/drivers/video/msm/mdss/mdss_fb.h new file mode 100644 index 00000000000..a1fd5321e9b --- /dev/null +++ b/drivers/video/msm/mdss/mdss_fb.h @@ -0,0 +1,96 @@ +/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef MDSS_FB_H +#define MDSS_FB_H + +#include +#include +#include +#include + +#include "mdss_mdp.h" +#include "mdss_panel.h" + +#define MSM_FB_DEFAULT_PAGE_SIZE 2 +#define MFD_KEY 0x11161126 +#define MSM_FB_MAX_DEV_LIST 32 + +#define MSM_FB_ENABLE_DBGFS + +#ifndef MAX +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif + +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +struct disp_info_type_suspend { + int op_enable; + int panel_power_on; +}; + +struct msm_fb_data_type { + u32 key; + u32 index; + u32 ref_cnt; + u32 fb_page; + + struct panel_id panel; + struct mdss_panel_info panel_info; + + u32 dest; + struct fb_info *fbi; + + int op_enable; + u32 fb_imgType; + + int hw_refresh; + + int overlay_play_enable; + + int panel_power_on; + struct disp_info_type_suspend suspend; + + int (*on_fnc) (struct msm_fb_data_type *mfd); + int (*off_fnc) (struct msm_fb_data_type *mfd); + void (*dma_fnc) (struct msm_fb_data_type *mfd); + int (*cursor_update) (struct fb_info *info, + struct fb_cursor *cursor); + int (*lut_update) (struct fb_info *info, + struct fb_cmap *cmap); + int (*do_histogram) (struct fb_info *info, + struct mdp_histogram *hist); + void *cursor_buf; + void *cursor_buf_phys; + + u32 bl_level; + struct mutex lock; + + struct platform_device *pdev; + + u32 var_xres; + u32 var_yres; + u32 var_pixclock; + + u32 mdp_fb_page_protection; + struct ion_client *iclient; + + struct mdss_mdp_ctl *ctl; +}; + +int mdss_fb_get_phys_info(unsigned long *start, unsigned long *len, int fb_num); +void mdss_fb_set_backlight(struct msm_fb_data_type *mfd, u32 bkl_lvl); +void mdss_fb_update_backlight(struct msm_fb_data_type *mfd); +#endif /* MDSS_FB_H */ diff --git a/drivers/video/msm/mdss/mdss_mdp.c b/drivers/video/msm/mdss/mdss_mdp.c new file mode 100644 index 00000000000..d1847c3d539 --- /dev/null +++ b/drivers/video/msm/mdss/mdss_mdp.c @@ -0,0 +1,494 @@ +/* + * MDSS MDP Interface (used by framebuffer core) + * + * Copyright (c) 2007-2012, Code Aurora Forum. All rights reserved. + * Copyright (C) 2007 Google Incorporated + * + * 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. + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "mdss.h" +#include "mdss_fb.h" +#include "mdss_mdp.h" + +/* 1.15 mdp clk factor */ +#define MDP_CLK_FACTOR(rate) (((rate) * 23) / 20) + +unsigned char *mdss_reg_base; + +struct mdss_res_type *mdss_res; +static struct msm_panel_common_pdata *mdp_pdata; + +static DEFINE_SPINLOCK(mdp_lock); +static DEFINE_MUTEX(mdp_clk_lock); +static DEFINE_MUTEX(mdp_suspend_mutex); + +u32 mdss_mdp_pipe_type_map[MDSS_MDP_MAX_SSPP] = { + MDSS_MDP_PIPE_TYPE_VIG, + MDSS_MDP_PIPE_TYPE_VIG, + MDSS_MDP_PIPE_TYPE_VIG, + MDSS_MDP_PIPE_TYPE_RGB, + MDSS_MDP_PIPE_TYPE_RGB, + MDSS_MDP_PIPE_TYPE_RGB, + MDSS_MDP_PIPE_TYPE_DMA, + MDSS_MDP_PIPE_TYPE_DMA, +}; + +u32 mdss_mdp_mixer_type_map[MDSS_MDP_MAX_LAYERMIXER] = { + MDSS_MDP_MIXER_TYPE_INTF, + MDSS_MDP_MIXER_TYPE_INTF, + MDSS_MDP_MIXER_TYPE_INTF, + MDSS_MDP_MIXER_TYPE_WRITEBACK, + MDSS_MDP_MIXER_TYPE_WRITEBACK, +}; + +irqreturn_t mdss_irq_handler(int mdss_irq, void *ptr) +{ + u32 intr = MDSS_MDP_REG_READ(MDSS_REG_HW_INTR_STATUS); + + mdss_res->irq_buzy = true; + + if (intr & MDSS_INTR_MDP) + mdss_mdp_isr(mdss_irq, ptr); + + mdss_res->irq_buzy = false; + + return IRQ_HANDLED; +} + +int mdss_mdp_irq_enable(u32 intr_type, u32 intf_num) +{ + u32 irq; + unsigned long irq_flags; + int ret = 0; + + if (intr_type == MDSS_MDP_IRQ_INTF_UNDER_RUN || + intr_type == MDSS_MDP_IRQ_INTF_VSYNC) + intf_num = intf_num << 1; + + irq = BIT(intr_type + intf_num); + + spin_lock_irqsave(&mdp_lock, irq_flags); + if (mdss_res->irq_mask & irq) { + pr_warn("MDSS IRQ-0x%x is already set, mask=%x irq=%d\n", + irq, mdss_res->irq_mask, mdss_res->irq_ena); + ret = -EBUSY; + } else { + mdss_res->irq_mask |= irq; + MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_CLEAR, irq); + MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_EN, mdss_res->irq_mask); + if (!mdss_res->irq_ena) { + mdss_res->irq_ena = true; + enable_irq(mdss_res->irq); + } + } + spin_unlock_irqrestore(&mdp_lock, irq_flags); + + return ret; +} + +void mdss_mdp_irq_disable(u32 intr_type, u32 intf_num) +{ + u32 irq; + unsigned long irq_flags; + + + if (intr_type == MDSS_MDP_IRQ_INTF_UNDER_RUN || + intr_type == MDSS_MDP_IRQ_INTF_VSYNC) + intf_num = intf_num << 1; + + irq = BIT(intr_type + intf_num); + + spin_lock_irqsave(&mdp_lock, irq_flags); + if (!(mdss_res->irq_mask & irq)) { + pr_warn("MDSS IRQ-%x is NOT set, mask=%x irq=%d\n", + irq, mdss_res->irq_mask, mdss_res->irq_ena); + } else { + mdss_res->irq_mask &= ~irq; + MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_EN, mdss_res->irq_mask); + if (!mdss_res->irq_mask) { + mdss_res->irq_ena = false; + disable_irq(mdss_res->irq); + } + } + spin_unlock_irqrestore(&mdp_lock, irq_flags); +} + +void mdss_mdp_irq_disable_nosync(u32 intr_type, u32 intf_num) +{ + u32 irq; + + if (intr_type == MDSS_MDP_IRQ_INTF_UNDER_RUN || + intr_type == MDSS_MDP_IRQ_INTF_VSYNC) + intf_num = intf_num << 1; + + irq = BIT(intr_type + intf_num); + + spin_lock(&mdp_lock); + if (!(mdss_res->irq_mask & irq)) { + pr_warn("MDSS IRQ-%x is NOT set, mask=%x irq=%d\n", + irq, mdss_res->irq_mask, mdss_res->irq_ena); + } else { + mdss_res->irq_mask &= ~irq; + MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_EN, mdss_res->irq_mask); + if (!mdss_res->irq_mask) { + mdss_res->irq_ena = false; + disable_irq_nosync(mdss_res->irq); + } + } + spin_unlock(&mdp_lock); +} + +static void mdss_mdp_clk_ctrl_update(int enable) +{ + if (mdss_res->clk_ena == enable) + return; + + pr_debug("MDP CLKS %s\n", (enable ? "Enable" : "Disable")); + mdss_res->clk_ena = enable; +} + +void mdss_mdp_clk_ctrl(int enable, int isr) +{ + static atomic_t clk_ref = ATOMIC_INIT(0); + static DEFINE_MUTEX(clk_ctrl_lock); + int force_off = 0; + + pr_debug("clk enable=%d isr=%d clk_ref=%d\n", enable, isr, + atomic_read(&clk_ref)); + /* + * It is assumed that if isr = TRUE then start = OFF + * if start = ON when isr = TRUE it could happen that the usercontext + * could turn off the clocks while the interrupt is updating the + * power to ON + */ + WARN_ON(isr == true && enable); + + if (enable) { + atomic_inc(&clk_ref); + } else if (!atomic_add_unless(&clk_ref, -1, 0)) { + pr_debug("master power-off req\n"); + force_off = 1; + } + + if (isr) { + /* if it's power off send workqueue to turn off clocks */ + if (mdss_res->clk_ena && !atomic_read(&clk_ref)) + queue_delayed_work(mdss_res->clk_ctrl_wq, + &mdss_res->clk_ctrl_worker, + mdss_res->timeout); + } else { + mutex_lock(&clk_ctrl_lock); + if (delayed_work_pending(&mdss_res->clk_ctrl_worker)) + cancel_delayed_work(&mdss_res->clk_ctrl_worker); + + if (atomic_read(&clk_ref)) { + mdss_mdp_clk_ctrl_update(true); + } else if (mdss_res->clk_ena) { + mutex_lock(&mdp_suspend_mutex); + if (force_off || mdss_res->suspend) { + mdss_mdp_clk_ctrl_update(false); + } else { + /* send workqueue to turn off mdp power */ + queue_delayed_work(mdss_res->clk_ctrl_wq, + &mdss_res->clk_ctrl_worker, + mdss_res->timeout); + } + mutex_unlock(&mdp_suspend_mutex); + } + mutex_unlock(&clk_ctrl_lock); + } +} + +static void mdss_mdp_clk_ctrl_workqueue_handler(struct work_struct *work) +{ + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); +} + +static int mdss_mdp_irq_clk_setup(void) +{ + int ret; + + ret = request_irq(mdss_res->irq, mdss_irq_handler, IRQF_DISABLED, + "MDSS", 0); + if (ret) { + pr_err("mdp request_irq() failed!\n"); + return ret; + } + disable_irq(mdss_res->irq); + + mdss_res->fs = regulator_get(NULL, "fs_mdp"); + if (IS_ERR(mdss_res->fs)) + mdss_res->fs = NULL; + else { + regulator_enable(mdss_res->fs); + mdss_res->fs_ena = true; + } + + return 0; +} + +static struct msm_panel_common_pdata *mdss_mdp_populate_pdata( + struct device *dev) +{ + struct msm_panel_common_pdata *pdata; + + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); + if (!pdata) + dev_err(dev, "could not allocate memory for pdata\n"); + return pdata; +} + +static u32 mdss_mdp_res_init(void) +{ + u32 rc; + + rc = mdss_mdp_irq_clk_setup(); + if (rc) + return rc; + + mdss_res->clk_ctrl_wq = create_singlethread_workqueue("mdp_clk_wq"); + INIT_DELAYED_WORK(&mdss_res->clk_ctrl_worker, + mdss_mdp_clk_ctrl_workqueue_handler); + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + mdss_res->rev = MDSS_MDP_REG_READ(MDSS_REG_HW_VERSION); + mdss_res->mdp_rev = MDSS_MDP_REG_READ(MDSS_MDP_REG_HW_VERSION); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + + mdss_res->smp_mb_cnt = MDSS_MDP_SMP_MMB_BLOCKS; + mdss_res->smp_mb_size = MDSS_MDP_SMP_MMB_SIZE; + mdss_res->pipe_type_map = mdss_mdp_pipe_type_map; + mdss_res->mixer_type_map = mdss_mdp_mixer_type_map; + + pr_info("mdss_revision=%x\n", mdss_res->rev); + pr_info("mdp_hw_revision=%x\n", mdss_res->mdp_rev); + + mdss_res->res_init = true; + mdss_res->timeout = HZ/20; + mdss_res->clk_ena = false; + mdss_res->irq_mask = MDSS_MDP_DEFAULT_INTR_MASK; + mdss_res->suspend = false; + mdss_res->prim_ptype = NO_PANEL; + mdss_res->irq_ena = false; + + return 0; +} + +static int mdss_mdp_probe(struct platform_device *pdev) +{ + struct resource *mdss_mdp_mres; + struct resource *mdss_mdp_ires; + resource_size_t size; + int rc; + + if (!mdss_res) { + mdss_res = devm_kzalloc(&pdev->dev, sizeof(*mdss_res), + GFP_KERNEL); + if (mdss_res == NULL) + return -ENOMEM; + } + + if (pdev->dev.of_node) { + pdev->id = 0; + mdp_pdata = mdss_mdp_populate_pdata(&pdev->dev); + mdss_mdp_mres = platform_get_resource(pdev, + IORESOURCE_MEM, 0); + mdss_mdp_ires = platform_get_resource(pdev, + IORESOURCE_IRQ, 0); + if (!mdss_mdp_mres || !mdss_mdp_ires) { + pr_err("unable to get the MDSS resources"); + rc = -ENOMEM; + goto probe_done; + } + mdss_reg_base = ioremap(mdss_mdp_mres->start, + resource_size(mdss_mdp_mres)); + + pr_info("MDP HW Base phy_Address=0x%x virt=0x%x\n", + (int) mdss_mdp_mres->start, + (int) mdss_reg_base); + + mdss_res->irq = mdss_mdp_ires->start; + } else if ((pdev->id == 0) && (pdev->num_resources > 0)) { + mdp_pdata = pdev->dev.platform_data; + + size = resource_size(&pdev->resource[0]); + mdss_reg_base = ioremap(pdev->resource[0].start, size); + + pr_info("MDP HW Base phy_Address=0x%x virt=0x%x\n", + (int) pdev->resource[0].start, + (int) mdss_reg_base); + + mdss_res->irq = platform_get_irq(pdev, 0); + if (mdss_res->irq < 0) { + pr_err("can not get mdp irq\n"); + rc = -ENOMEM; + goto probe_done; + } + } + + if (unlikely(!mdss_reg_base)) { + rc = -ENOMEM; + goto probe_done; + } + + rc = mdss_mdp_res_init(); + if (rc) { + pr_err("unable to initialize mdss mdp resources\n"); + goto probe_done; + } + +probe_done: + if (IS_ERR_VALUE(rc)) { + if (mdss_res) { + devm_kfree(&pdev->dev, mdss_res); + mdss_res = NULL; + } + } + + return rc; +} + +void mdss_mdp_footswitch_ctrl(int on) +{ + mutex_lock(&mdp_suspend_mutex); + if (!mdss_res->suspend || mdss_res->eintf_ena || !mdss_res->fs) { + mutex_unlock(&mdp_suspend_mutex); + return; + } + + if (on && !mdss_res->fs_ena) { + pr_debug("Enable MDP FS\n"); + regulator_enable(mdss_res->fs); + mdss_res->fs_ena = true; + } else if (!on && mdss_res->fs_ena) { + pr_debug("Disable MDP FS\n"); + regulator_disable(mdss_res->fs); + mdss_res->fs_ena = false; + } + mutex_unlock(&mdp_suspend_mutex); +} + +#ifdef CONFIG_PM +static void mdss_mdp_suspend_sub(void) +{ + cancel_delayed_work(&mdss_res->clk_ctrl_worker); + + flush_workqueue(mdss_res->clk_ctrl_wq); + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + + mutex_lock(&mdp_suspend_mutex); + mdss_res->suspend = true; + mutex_unlock(&mdp_suspend_mutex); +} + +static int mdss_mdp_suspend(struct platform_device *pdev, pm_message_t state) +{ + if (pdev->id == 0) { + mdss_mdp_suspend_sub(); + if (mdss_res->clk_ena) { + pr_err("MDP suspend failed\n"); + return -EBUSY; + } + mdss_mdp_footswitch_ctrl(false); + } + return 0; +} + +static int mdss_mdp_resume(struct platform_device *pdev) +{ + mdss_mdp_footswitch_ctrl(true); + mutex_lock(&mdp_suspend_mutex); + mdss_res->suspend = false; + mutex_unlock(&mdp_suspend_mutex); + return 0; +} +#else +#define mdss_mdp_suspend NULL +#define mdss_mdp_resume NULL +#endif + +static int mdss_mdp_remove(struct platform_device *pdev) +{ + if (mdss_res->fs != NULL) + regulator_put(mdss_res->fs); + iounmap(mdss_reg_base); + pm_runtime_disable(&pdev->dev); + return 0; +} + +static const struct of_device_id mdss_mdp_dt_match[] = { + { .compatible = "qcom,mdss_mdp",}, +}; +MODULE_DEVICE_TABLE(of, mdss_mdp_dt_match); + +static struct platform_driver mdss_mdp_driver = { + .probe = mdss_mdp_probe, + .remove = mdss_mdp_remove, + .suspend = mdss_mdp_suspend, + .resume = mdss_mdp_resume, + .shutdown = NULL, + .driver = { + /* + * Driver name must match the device name added in + * platform.c. + */ + .name = "mdp", + .of_match_table = mdss_mdp_dt_match, + }, +}; + +static int mdss_mdp_register_driver(void) +{ + return platform_driver_register(&mdss_mdp_driver); +} + +static int __init mdss_mdp_driver_init(void) +{ + int ret; + + ret = mdss_mdp_register_driver(); + if (ret) { + pr_err("mdp_register_driver() failed!\n"); + return ret; + } + + return 0; + +} + +module_init(mdss_mdp_driver_init); diff --git a/drivers/video/msm/mdss/mdss_mdp.h b/drivers/video/msm/mdss/mdss_mdp.h new file mode 100644 index 00000000000..39d65201808 --- /dev/null +++ b/drivers/video/msm/mdss/mdss_mdp.h @@ -0,0 +1,289 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef MDSS_MDP_H +#define MDSS_MDP_H + +#include +#include +#include + +#include "mdss.h" +#include "mdss_mdp_hwio.h" + +#define MDSS_MDP_DEFAULT_INTR_MASK 0 +#define MDSS_MDP_CURSOR_WIDTH 64 +#define MDSS_MDP_CURSOR_HEIGHT 64 +#define MDSS_MDP_CURSOR_SIZE (MDSS_MDP_CURSOR_WIDTH*MDSS_MDP_CURSOR_WIDTH*4) + +#define MDP_CLK_DEFAULT_RATE 37500000 +#define PHASE_STEP_SHIFT 21 +#define MAX_MIXER_WIDTH 2048 +#define MAX_MIXER_HEIGHT 2048 +#define MAX_IMG_WIDTH 0x3FFF +#define MAX_IMG_HEIGHT 0x3FFF +#define MIN_DST_W 10 +#define MIN_DST_H 10 +#define MAX_DST_W MAX_MIXER_WIDTH +#define MAX_DST_H MAX_MIXER_HEIGHT +#define MAX_PLANES 4 +#define MAX_DOWNSCALE_RATIO 4 +#define MAX_UPSCALE_RATIO 20 + +#ifdef MDSS_MDP_DEBUG_REG +static inline void mdss_mdp_reg_write(u32 addr, u32 val) +{ + pr_debug("0x%05X = 0x%08X\n", addr, val); + MDSS_REG_WRITE(addr, val); +} +#define MDSS_MDP_REG_WRITE(addr, val) mdss_mdp_reg_write((u32)addr, (u32)(val)) +static inline u32 mdss_mdp_reg_read(u32 addr) +{ + u32 val; + val = MDSS_REG_READ(addr); + pr_debug("0x%05X = 0x%08X\n", addr, val); + return val; +} +#define MDSS_MDP_REG_READ(addr) mdss_mdp_reg_read((u32)(addr)) +#else +#define MDSS_MDP_REG_WRITE(addr, val) MDSS_REG_WRITE((u32)(addr), (u32)(val)) +#define MDSS_MDP_REG_READ(addr) MDSS_REG_READ((u32)(addr)) +#endif + +enum mdss_mdp_block_power_state { + MDP_BLOCK_POWER_OFF, + MDP_BLOCK_POWER_ON +}; + +enum mdss_mdp_mixer_type { + MDSS_MDP_MIXER_TYPE_UNUSED, + MDSS_MDP_MIXER_TYPE_INTF, + MDSS_MDP_MIXER_TYPE_WRITEBACK, +}; + +enum mdss_mdp_mixer_mux { + MDSS_MDP_MIXER_MUX_DEFAULT, + MDSS_MDP_MIXER_MUX_LEFT, + MDSS_MDP_MIXER_MUX_RIGHT, +}; + +enum mdss_mdp_pipe_type { + MDSS_MDP_PIPE_TYPE_UNUSED, + MDSS_MDP_PIPE_TYPE_VIG, + MDSS_MDP_PIPE_TYPE_RGB, + MDSS_MDP_PIPE_TYPE_DMA, +}; + +enum mdss_mdp_block_type { + MDSS_MDP_BLOCK_UNUSED, + MDSS_MDP_BLOCK_SSPP, + MDSS_MDP_BLOCK_MIXER, + MDSS_MDP_BLOCK_DSPP, + MDSS_MDP_BLOCK_WB, + MDSS_MDP_BLOCK_MAX +}; + +struct mdss_mdp_ctl { + u32 num; + u32 ref_cnt; + + u32 intf_num; + u32 intf_type; + + u32 opmode; + u32 flush_bits; + + u32 play_cnt; + + u16 width; + u16 height; + + struct msm_fb_data_type *mfd; + struct mdss_mdp_mixer *mixer_left; + struct mdss_mdp_mixer *mixer_right; + struct mutex lock; + + int (*start_fnc) (struct mdss_mdp_ctl *ctl); + int (*stop_fnc) (struct mdss_mdp_ctl *ctl); + int (*prepare_fnc) (struct mdss_mdp_ctl *ctl, void *arg); + int (*display_fnc) (struct mdss_mdp_ctl *ctl, void *arg); + + void *priv_data; +}; + +struct mdss_mdp_mixer { + u32 num; + u32 ref_cnt; + u8 type; + u8 params_changed; + + u16 width; + u16 height; + u8 cursor_enabled; + u8 rotator_mode; + + struct mdss_mdp_ctl *ctl; + struct mdss_mdp_pipe *stage_pipe[MDSS_MDP_MAX_STAGE]; +}; + +struct mdss_mdp_img_rect { + u16 x; + u16 y; + u16 w; + u16 h; +}; + +struct mdss_mdp_format_params { + u32 format; + u8 is_yuv; + + u8 frame_format; + u8 chroma_sample; + u8 solid_fill; + u8 fetch_planes; + u8 unpack_align_msb; /* 0 to LSB, 1 to MSB */ + u8 unpack_tight; /* 0 for loose, 1 for tight */ + u8 unpack_count; /* 0 = 1 component, 1 = 2 component ... */ + u8 bpp; + u8 alpha_enable; /* source has alpha */ + + /* + * number of bits for source component, + * 0 = 1 bit, 1 = 2 bits, 2 = 6 bits, 3 = 8 bits + */ + u8 a_bit; /* component 3, alpha */ + u8 r_bit; /* component 2, R_Cr */ + u8 b_bit; /* component 1, B_Cb */ + u8 g_bit; /* component 0, G_lumz */ + + /* + * unpack pattern + * A = C3, R = C2, B = C1, G = C0 + */ + u8 element3; + u8 element2; + u8 element1; + u8 element0; +}; + +struct mdss_mdp_plane_sizes { + u32 num_planes; + u32 plane_size[MAX_PLANES]; + u32 total_size; + u32 ystride[MAX_PLANES]; +}; + +struct mdss_mdp_img_data { + u32 addr; + u32 len; + u32 flags; + int p_need; + struct file *srcp_file; + struct ion_handle *srcp_ihdl; + struct ion_client *iclient; +}; + +struct mdss_mdp_data { + u8 num_planes; + u8 bwc_enabled; + struct mdss_mdp_img_data p[MAX_PLANES]; +}; + +struct mdss_mdp_pipe { + u32 num; + u32 type; + u32 ndx; + atomic_t ref_cnt; + u32 play_cnt; + + u32 flags; + u32 bwc_mode; + + u16 img_width; + u16 img_height; + struct mdss_mdp_img_rect src; + struct mdss_mdp_img_rect dst; + + struct mdss_mdp_format_params *src_fmt; + struct mdss_mdp_plane_sizes src_planes; + + u8 mixer_stage; + u8 is_fg; + u8 alpha; + u32 transp; + + struct msm_fb_data_type *mfd; + struct mdss_mdp_mixer *mixer; + struct mutex lock; + + struct mdp_overlay req_data; + u32 params_changed; + + unsigned long smp[MAX_PLANES]; +}; + +static inline void mdss_mdp_ctl_write(struct mdss_mdp_ctl *ctl, + u32 reg, u32 val) +{ + int offset = MDSS_MDP_REG_CTL_OFFSET(ctl->num); + MDSS_MDP_REG_WRITE(offset + reg, val); +} + +static inline u32 mdss_mdp_ctl_read(struct mdss_mdp_ctl *ctl, u32 reg) +{ + int offset = MDSS_MDP_REG_CTL_OFFSET(ctl->num); + return MDSS_MDP_REG_READ(offset + reg); +} + +irqreturn_t mdss_mdp_isr(int irq, void *ptr); +int mdss_mdp_irq_enable(u32 intr_type, u32 intf_num); +void mdss_mdp_irq_disable(u32 intr_type, u32 intf_num); +void mdss_mdp_irq_disable_nosync(u32 intr_type, u32 intf_num); +int mdss_mdp_set_intr_callback(u32 intr_type, u32 intf_num, + void (*fnc_ptr)(void *), void *arg); + +unsigned long mdss_mdp_get_clk_rate(u32 clk_idx); +int mdss_mdp_vsync_clk_enable(int enable); +void mdss_mdp_clk_ctrl(int enable, int isr); +void mdss_mdp_footswitch_ctrl(int on); + +int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd); + +int mdss_mdp_ctl_on(struct msm_fb_data_type *mfd); +int mdss_mdp_ctl_off(struct msm_fb_data_type *mfd); + +struct mdss_mdp_mixer *mdss_mdp_mixer_get(struct mdss_mdp_ctl *ctl, int mux); +struct mdss_mdp_pipe *mdss_mdp_mixer_stage_pipe(struct mdss_mdp_ctl *ctl, + int mux, int stage); +int mdss_mdp_mixer_pipe_update(struct mdss_mdp_pipe *pipe, int params_changed); +int mdss_mdp_mixer_pipe_unstage(struct mdss_mdp_pipe *pipe); +int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg); + +struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_pnum(u32 pnum); +struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_locked(u32 type); +struct mdss_mdp_pipe *mdss_mdp_pipe_get_locked(u32 ndx); +int mdss_mdp_pipe_lock(struct mdss_mdp_pipe *pipe); +void mdss_mdp_pipe_unlock(struct mdss_mdp_pipe *pipe); + +int mdss_mdp_pipe_destroy(struct mdss_mdp_pipe *pipe); +int mdss_mdp_pipe_release_all(struct msm_fb_data_type *mfd); +int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe, + struct mdss_mdp_data *src_data); + +int mdss_mdp_data_check(struct mdss_mdp_data *data, + struct mdss_mdp_plane_sizes *ps); +int mdss_mdp_get_plane_sizes(u32 format, u32 w, u32 h, + struct mdss_mdp_plane_sizes *ps); +struct mdss_mdp_format_params *mdss_mdp_get_format_params(u32 format); + +#endif /* MDSS_MDP_H */ diff --git a/drivers/video/msm/mdss/mdss_mdp_ctl.c b/drivers/video/msm/mdss/mdss_mdp_ctl.c new file mode 100644 index 00000000000..182cffc7326 --- /dev/null +++ b/drivers/video/msm/mdss/mdss_mdp_ctl.c @@ -0,0 +1,598 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include + +#include "mdss_fb.h" +#include "mdss_mdp.h" + +static DEFINE_MUTEX(mdss_mdp_ctl_lock); +static struct mdss_mdp_ctl mdss_mdp_ctl_list[MDSS_MDP_MAX_CTL]; +static struct mdss_mdp_mixer mdss_mdp_mixer_list[MDSS_MDP_MAX_LAYERMIXER]; + +static struct mdss_mdp_ctl *mdss_mdp_ctl_alloc(void) +{ + struct mdss_mdp_ctl *ctl = NULL; + int cnum; + + mutex_lock(&mdss_mdp_ctl_lock); + for (cnum = 0; cnum < MDSS_MDP_MAX_CTL; cnum++) { + if (mdss_mdp_ctl_list[cnum].ref_cnt == 0) { + ctl = &mdss_mdp_ctl_list[cnum]; + ctl->num = cnum; + ctl->ref_cnt++; + mutex_init(&ctl->lock); + + pr_debug("alloc ctl_num=%d\n", ctl->num); + break; + } + } + mutex_unlock(&mdss_mdp_ctl_lock); + + return ctl; +} + +static int mdss_mdp_ctl_free(struct mdss_mdp_ctl *ctl) +{ + if (!ctl) + return -ENODEV; + + pr_debug("free ctl_num=%d ref_cnt=%d\n", ctl->num, ctl->ref_cnt); + + if (!ctl->ref_cnt) { + pr_err("called with ref_cnt=0\n"); + return -EINVAL; + } + + mutex_lock(&mdss_mdp_ctl_lock); + if (--ctl->ref_cnt == 0) + memset(ctl, 0, sizeof(*ctl)); + mutex_unlock(&mdss_mdp_ctl_lock); + + return 0; +} + +static struct mdss_mdp_mixer *mdss_mdp_mixer_alloc(u32 type) +{ + struct mdss_mdp_mixer *mixer = NULL; + int mnum; + + mutex_lock(&mdss_mdp_ctl_lock); + for (mnum = 0; mnum < MDSS_MDP_MAX_LAYERMIXER; mnum++) { + if (type == mdss_res->mixer_type_map[mnum] && + mdss_mdp_mixer_list[mnum].ref_cnt == 0) { + mixer = &mdss_mdp_mixer_list[mnum]; + mixer->num = mnum; + mixer->ref_cnt++; + mixer->params_changed++; + mixer->type = type; + + pr_debug("mixer_num=%d\n", mixer->num); + break; + } + } + mutex_unlock(&mdss_mdp_ctl_lock); + + return mixer; +} + +static int mdss_mdp_mixer_free(struct mdss_mdp_mixer *mixer) +{ + if (!mixer) + return -ENODEV; + + pr_debug("free mixer_num=%d ref_cnt=%d\n", mixer->num, mixer->ref_cnt); + + if (!mixer->ref_cnt) { + pr_err("called with ref_cnt=0\n"); + return -EINVAL; + } + + mutex_lock(&mdss_mdp_ctl_lock); + if (--mixer->ref_cnt == 0) + memset(mixer, 0, sizeof(*mixer)); + mutex_unlock(&mdss_mdp_ctl_lock); + + return 0; +} + +static int mdss_mdp_ctl_init(struct msm_fb_data_type *mfd) +{ + struct mdss_mdp_ctl *ctl; + u32 width, height; + + if (!mfd) + return -ENODEV; + + width = mfd->fbi->var.xres; + height = mfd->fbi->var.yres; + + if (width > (2 * MAX_MIXER_WIDTH)) { + pr_err("unsupported resolution\n"); + return -EINVAL; + } + + ctl = mdss_mdp_ctl_alloc(); + + if (!ctl) { + pr_err("unable to allocate ctl\n"); + return -ENOMEM; + } + + ctl->mfd = mfd; + ctl->width = width; + ctl->height = height; + + ctl->mixer_left = mdss_mdp_mixer_alloc(MDSS_MDP_MIXER_TYPE_INTF); + if (!ctl->mixer_left) { + pr_err("unable to allocate layer mixer\n"); + mdss_mdp_ctl_free(ctl); + return -ENOMEM; + } + + ctl->mixer_left->width = MIN(width, MAX_MIXER_WIDTH); + ctl->mixer_left->height = height; + ctl->mixer_left->ctl = ctl; + + width -= ctl->mixer_left->width; + + if (width) { + ctl->mixer_right = + mdss_mdp_mixer_alloc(MDSS_MDP_MIXER_TYPE_INTF); + if (!ctl->mixer_right) { + pr_err("unable to allocate layer mixer\n"); + mdss_mdp_mixer_free(ctl->mixer_left); + mdss_mdp_ctl_free(ctl); + return -ENOMEM; + } + ctl->mixer_right->width = width; + ctl->mixer_right->height = height; + ctl->mixer_right->ctl = ctl; + } + + switch (mfd->panel_info.type) { + case WRITEBACK_PANEL: + ctl->intf_num = MDSS_MDP_NO_INTF; + ctl->opmode = MDSS_MDP_CTL_OP_WFD_MODE; + break; + default: + pr_err("unsupported panel type (%d)\n", mfd->panel_info.type); + mdss_mdp_ctl_free(ctl); + return -EINVAL; + + } + + ctl->opmode |= (ctl->intf_num << 4); + + if (ctl->mixer_right) { + ctl->opmode |= MDSS_MDP_CTL_OP_PACK_3D_ENABLE | + MDSS_MDP_CTL_OP_PACK_3D_H_ROW_INT; + } + + mfd->ctl = ctl; + + return 0; +} + +static int mdss_mdp_ctl_destroy(struct msm_fb_data_type *mfd) +{ + struct mdss_mdp_ctl *ctl; + if (!mfd || !mfd->ctl) + return -ENODEV; + + ctl = mfd->ctl; + mfd->ctl = NULL; + + if (ctl->mixer_left) + mdss_mdp_mixer_free(ctl->mixer_left); + if (ctl->mixer_right) + mdss_mdp_mixer_free(ctl->mixer_right); + mdss_mdp_ctl_free(ctl); + + return 0; +} + +int mdss_mdp_ctl_on(struct msm_fb_data_type *mfd) +{ + struct mdss_panel_data *pdata; + struct mdss_mdp_ctl *ctl; + struct mdss_mdp_mixer *mixer; + u32 outsize, temp, off; + int ret = 0; + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + pdata = dev_get_platdata(&mfd->pdev->dev); + if (!pdata) { + pr_err("no panel connected\n"); + return -ENODEV; + } + + if (!mfd->ctl) { + if (mdss_mdp_ctl_init(mfd)) { + pr_err("unable to initialize ctl\n"); + return -ENODEV; + } + } + ctl = mfd->ctl; + + mutex_lock(&ctl->lock); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + if (ctl->start_fnc) + ret = ctl->start_fnc(ctl); + else + pr_warn("no start function for ctl=%d type=%d\n", ctl->num, + mfd->panel_info.type); + + if (ret) { + pr_err("unable to start intf\n"); + goto start_fail; + } + + pr_debug("ctl_num=%d\n", ctl->num); + + mixer = ctl->mixer_left; + mixer->params_changed++; + + temp = MDSS_MDP_REG_READ(MDSS_MDP_REG_DISP_INTF_SEL); + temp |= (ctl->intf_type << (ctl->intf_num * 8)); + MDSS_MDP_REG_WRITE(MDSS_MDP_REG_DISP_INTF_SEL, temp); + + outsize = (mixer->height << 16) | mixer->width; + off = MDSS_MDP_REG_LM_OFFSET(mixer->num); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OUT_SIZE, outsize); + + if (ctl->mixer_right) { + mixer = ctl->mixer_right; + mixer->params_changed++; + outsize = (mixer->height << 16) | mixer->width; + off = MDSS_MDP_REG_LM_OFFSET(mixer->num); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OUT_SIZE, outsize); + mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_PACK_3D, 0); + } + + ret = pdata->on(pdata); + +start_fail: + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + mutex_unlock(&ctl->lock); + + return ret; +} + +int mdss_mdp_ctl_off(struct msm_fb_data_type *mfd) +{ + struct mdss_panel_data *pdata; + struct mdss_mdp_ctl *ctl; + int ret = 0; + + if (!mfd) + return -ENODEV; + + if (mfd->key != MFD_KEY) + return -EINVAL; + + if (!mfd->ctl) { + pr_err("ctl not initialized\n"); + return -ENODEV; + } + + pdata = dev_get_platdata(&mfd->pdev->dev); + if (!pdata) { + pr_err("no panel connected\n"); + return -ENODEV; + } + + ctl = mfd->ctl; + + pr_debug("ctl_num=%d\n", mfd->ctl->num); + + mutex_lock(&ctl->lock); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + if (ctl->stop_fnc) + ret = ctl->stop_fnc(ctl); + else + pr_warn("no stop func for ctl=%d\n", ctl->num); + + if (ret) + pr_warn("error powering off intf ctl=%d\n", ctl->num); + + ret = pdata->off(pdata); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + + ctl->play_cnt = 0; + mutex_unlock(&ctl->lock); + + mdss_mdp_pipe_release_all(mfd); + + if (!mfd->ref_cnt) + mdss_mdp_ctl_destroy(mfd); + + + return ret; +} + +static int mdss_mdp_mixer_setup(struct mdss_mdp_ctl *ctl, + struct mdss_mdp_mixer *mixer) +{ + struct mdss_mdp_pipe *pipe, *bgpipe = NULL; + u32 off, blend_op, blend_stage; + u32 mixercfg = 0, blend_color_out = 0, bgalpha = 0; + int stage; + + if (!mixer) + return -ENODEV; + + pr_debug("setup mixer=%d\n", mixer->num); + + for (stage = MDSS_MDP_STAGE_BASE; stage < MDSS_MDP_MAX_STAGE; stage++) { + pipe = mixer->stage_pipe[stage]; + if (pipe == NULL) { + if (stage == MDSS_MDP_STAGE_BASE) + mixercfg |= MDSS_MDP_LM_BORDER_COLOR; + continue; + } + + if (stage != pipe->mixer_stage) { + mixer->stage_pipe[stage] = NULL; + continue; + } + mixercfg |= stage << (3 * pipe->num); + + if (stage == MDSS_MDP_STAGE_BASE) { + bgpipe = pipe; + if (pipe->src_fmt->alpha_enable) + bgalpha = 1; + continue; + } + + blend_stage = stage - MDSS_MDP_STAGE_0; + off = MDSS_MDP_REG_LM_OFFSET(mixer->num) + + MDSS_MDP_REG_LM_BLEND_OFFSET(blend_stage); + + if (pipe->is_fg) { + bgalpha = 0; + if (bgpipe) { + mixercfg &= ~(0x7 << (3 * bgpipe->num)); + mixercfg |= MDSS_MDP_LM_BORDER_COLOR; + } + blend_op = (MDSS_MDP_BLEND_FG_ALPHA_FG_CONST | + MDSS_MDP_BLEND_BG_ALPHA_BG_CONST); + /* keep fg alpha */ + blend_color_out |= 1 << (blend_stage + 1); + + pr_debug("pnum=%d stg=%d alpha=IS_FG\n", pipe->num, + stage); + } else if (pipe->src_fmt->alpha_enable) { + bgalpha = 0; + blend_op = (MDSS_MDP_BLEND_BG_ALPHA_FG_PIXEL | + MDSS_MDP_BLEND_BG_INV_ALPHA); + /* keep fg alpha */ + blend_color_out |= 1 << (blend_stage + 1); + + pr_debug("pnum=%d stg=%d alpha=FG PIXEL\n", pipe->num, + stage); + } else if (bgalpha) { + blend_op = (MDSS_MDP_BLEND_BG_ALPHA_BG_PIXEL | + MDSS_MDP_BLEND_FG_ALPHA_BG_PIXEL | + MDSS_MDP_BLEND_FG_INV_ALPHA); + /* keep bg alpha */ + pr_debug("pnum=%d stg=%d alpha=BG_PIXEL\n", pipe->num, + stage); + } else { + blend_op = (MDSS_MDP_BLEND_FG_ALPHA_FG_CONST | + MDSS_MDP_BLEND_BG_ALPHA_BG_CONST); + pr_debug("pnum=%d stg=%d alpha=CONST\n", pipe->num, + stage); + } + + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OP_MODE, blend_op); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_BLEND_FG_ALPHA, + pipe->alpha); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_BLEND_BG_ALPHA, + 0xFF - pipe->alpha); + } + + if (mixer->cursor_enabled) + mixercfg |= MDSS_MDP_LM_CURSOR_OUT; + + pr_debug("mixer=%d mixer_cfg=%x\n", mixer->num, mixercfg); + + ctl->flush_bits |= BIT(6) << mixer->num; /* LAYER_MIXER */ + + off = MDSS_MDP_REG_LM_OFFSET(mixer->num); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_OP_MODE, blend_color_out); + mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_LAYER(mixer->num), mixercfg); + + return 0; +} + +struct mdss_mdp_mixer *mdss_mdp_mixer_get(struct mdss_mdp_ctl *ctl, int mux) +{ + struct mdss_mdp_mixer *mixer = NULL; + if (!ctl) + return NULL; + + switch (mux) { + case MDSS_MDP_MIXER_MUX_DEFAULT: + case MDSS_MDP_MIXER_MUX_LEFT: + mixer = ctl->mixer_left; + break; + case MDSS_MDP_MIXER_MUX_RIGHT: + mixer = ctl->mixer_right; + break; + } + + return mixer; +} + +struct mdss_mdp_pipe *mdss_mdp_mixer_stage_pipe(struct mdss_mdp_ctl *ctl, + int mux, int stage) +{ + struct mdss_mdp_pipe *pipe = NULL; + struct mdss_mdp_mixer *mixer; + if (!ctl) + return NULL; + + if (mutex_lock_interruptible(&ctl->lock)) + return NULL; + + mixer = mdss_mdp_mixer_get(ctl, mux); + if (mixer) + pipe = mixer->stage_pipe[stage]; + mutex_unlock(&ctl->lock); + + return pipe; +} + +int mdss_mdp_mixer_pipe_update(struct mdss_mdp_pipe *pipe, int params_changed) +{ + struct mdss_mdp_ctl *ctl; + struct mdss_mdp_mixer *mixer; + + if (!pipe) + return -EINVAL; + mixer = pipe->mixer; + if (!mixer) + return -EINVAL; + ctl = mixer->ctl; + if (!ctl) + return -EINVAL; + + if (pipe->mixer_stage >= MDSS_MDP_MAX_STAGE) { + pr_err("invalid mixer stage\n"); + return -EINVAL; + } + + pr_debug("pnum=%x mixer=%d stage=%d\n", pipe->num, mixer->num, + pipe->mixer_stage); + + if (mutex_lock_interruptible(&ctl->lock)) + return -EINTR; + + if (params_changed) { + mixer->params_changed++; + mixer->stage_pipe[pipe->mixer_stage] = pipe; + } + + if (pipe->type == MDSS_MDP_PIPE_TYPE_DMA) + ctl->flush_bits |= BIT(pipe->num) << 5; + else /* RGB/VIG pipe */ + ctl->flush_bits |= BIT(pipe->num); + + mutex_unlock(&ctl->lock); + + return 0; +} + +int mdss_mdp_mixer_pipe_unstage(struct mdss_mdp_pipe *pipe) +{ + struct mdss_mdp_ctl *ctl; + struct mdss_mdp_mixer *mixer; + + if (!pipe) + return -EINVAL; + mixer = pipe->mixer; + if (!mixer) + return -EINVAL; + ctl = mixer->ctl; + if (!ctl) + return -EINVAL; + + pr_debug("unstage pnum=%d stage=%d mixer=%d\n", pipe->num, + pipe->mixer_stage, mixer->num); + + if (mutex_lock_interruptible(&ctl->lock)) + return -EINTR; + + mixer->params_changed++; + mixer->stage_pipe[pipe->mixer_stage] = NULL; + + mutex_unlock(&ctl->lock); + + return 0; +} + +static int mdss_mdp_mixer_update(struct mdss_mdp_mixer *mixer) +{ + mixer->params_changed = 0; + + /* skip mixer setup for rotator */ + if (!mixer->rotator_mode) + mdss_mdp_mixer_setup(mixer->ctl, mixer); + + return 0; +} + +int mdss_mdp_display_commit(struct mdss_mdp_ctl *ctl, void *arg) +{ + int mixer1_changed, mixer2_changed; + int ret = 0; + + if (!ctl) { + pr_err("display function not set\n"); + return -ENODEV; + } + + pr_debug("commit ctl=%d\n", ctl->num); + + if (mutex_lock_interruptible(&ctl->lock)) + return -EINTR; + + mixer1_changed = (ctl->mixer_left && ctl->mixer_left->params_changed); + mixer2_changed = (ctl->mixer_right && ctl->mixer_right->params_changed); + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + if (mixer1_changed || mixer2_changed) { + if (ctl->prepare_fnc) + ret = ctl->prepare_fnc(ctl, arg); + if (ret) { + pr_err("error preparing display\n"); + goto done; + } + + if (mixer1_changed) + mdss_mdp_mixer_update(ctl->mixer_left); + if (mixer2_changed) + mdss_mdp_mixer_update(ctl->mixer_right); + + mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_TOP, ctl->opmode); + ctl->flush_bits |= BIT(17); /* CTL */ + } + + mdss_mdp_ctl_write(ctl, MDSS_MDP_REG_CTL_FLUSH, ctl->flush_bits); + wmb(); + ctl->flush_bits = 0; + + if (ctl->display_fnc) + ret = ctl->display_fnc(ctl, arg); /* kickoff */ + if (ret) + pr_warn("error displaying frame\n"); + + ctl->play_cnt++; + +done: + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + + mutex_unlock(&ctl->lock); + + return ret; +} diff --git a/drivers/video/msm/mdss/mdss_mdp_formats.h b/drivers/video/msm/mdss/mdss_mdp_formats.h new file mode 100644 index 00000000000..07eefc1c270 --- /dev/null +++ b/drivers/video/msm/mdss/mdss_mdp_formats.h @@ -0,0 +1,328 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef MDSS_MDP_FORMATS_H +#define MDSS_MDP_FORMATS_H + +#include + +#include "mdss_mdp.h" + +#define C3_ALPHA 3 /* alpha */ +#define C2_R_Cr 2 /* R/Cr */ +#define C1_B_Cb 1 /* B/Cb */ +#define C0_G_Y 0 /* G/luma */ + +static struct mdss_mdp_format_params mdss_mdp_format_map[MDP_IMGTYPE_LIMIT] = { + [MDP_RGB_565] = { + .format = MDP_RGB_565, + .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED, + .a_bit = 0, + .r_bit = 1, /* R, 5 bits */ + .b_bit = 1, /* B, 5 bits */ + .g_bit = 2, /* G, 6 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 2, + .element2 = C2_R_Cr, + .element1 = C0_G_Y, + .element0 = C1_B_Cb, + .bpp = 1, /* 2 bpp */ + }, + [MDP_BGR_565] = { + .format = MDP_BGR_565, + .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED, + .a_bit = 0, + .r_bit = 1, /* R, 5 bits */ + .b_bit = 1, /* B, 5 bits */ + .g_bit = 2, /* G, 6 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 2, + .element2 = C1_B_Cb, + .element1 = C0_G_Y, + .element0 = C2_R_Cr, + .bpp = 1, /* 2 bpp */ + }, + [MDP_RGB_888] = { + .format = MDP_RGB_888, + .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED, + .a_bit = 0, + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 2, + .element2 = C1_B_Cb, + .element1 = C0_G_Y, + .element0 = C2_R_Cr, + .bpp = 2, /* 3 bpp */ + }, + [MDP_XRGB_8888] = { + .format = MDP_XRGB_8888, + .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED, + .a_bit = 3, /* alpha, 4 bits */ + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 3, + .element3 = C1_B_Cb, + .element2 = C0_G_Y, + .element1 = C2_R_Cr, + .element0 = C3_ALPHA, + .bpp = 3, /* 4 bpp */ + }, + [MDP_ARGB_8888] = { + .format = MDP_ARGB_8888, + .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED, + .a_bit = 3, /* alpha, 4 bits */ + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 1, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 3, + .element3 = C1_B_Cb, + .element2 = C0_G_Y, + .element1 = C2_R_Cr, + .element0 = C3_ALPHA, + .bpp = 3, /* 4 bpp */ + }, + [MDP_RGBA_8888] = { + .format = MDP_RGBA_8888, + .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED, + .a_bit = 3, /* alpha, 4 bits */ + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 1, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 3, + .element3 = C3_ALPHA, + .element2 = C1_B_Cb, + .element1 = C0_G_Y, + .element0 = C2_R_Cr, + .bpp = 3, /* 4 bpp */ + }, + [MDP_RGBX_8888] = { + .format = MDP_RGBX_8888, + .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED, + .a_bit = 3, + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 3, + .element3 = C3_ALPHA, + .element2 = C1_B_Cb, + .element1 = C0_G_Y, + .element0 = C2_R_Cr, + .bpp = 3, /* 4 bpp */ + }, + [MDP_BGRA_8888] = { + .format = MDP_BGRA_8888, + .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED, + .a_bit = 3, /* alpha, 4 bits */ + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 1, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 3, + .element3 = C3_ALPHA, + .element2 = C2_R_Cr, + .element1 = C0_G_Y, + .element0 = C1_B_Cb, + .bpp = 3, /* 4 bpp */ + }, + [MDP_YCRYCB_H2V1] = { + .format = MDP_YCRYCB_H2V1, + .is_yuv = 1, + .a_bit = 0, + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 1, /* 2 */ + .bpp = 1, /* 2 bpp */ + .fetch_planes = MDSS_MDP_PLANE_INTERLEAVED, + .chroma_sample = MDSS_MDP_CHROMA_H2V1, + .unpack_count = 3, + .element3 = C0_G_Y, + .element2 = C2_R_Cr, + .element1 = C0_G_Y, + .element0 = C1_B_Cb, + }, + [MDP_Y_CRCB_H2V1] = { + .format = MDP_Y_CRCB_H2V1, + .is_yuv = 1, + .a_bit = 0, + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 1, /* 2 */ + .bpp = 1, /* 2 bpp */ + .fetch_planes = MDSS_MDP_PLANE_PSEUDO_PLANAR, + .chroma_sample = MDSS_MDP_CHROMA_H2V1, + .element1 = C1_B_Cb, + .element0 = C2_R_Cr, + }, + [MDP_Y_CBCR_H2V1] = { + .format = MDP_Y_CBCR_H2V1, + .is_yuv = 1, + .a_bit = 0, + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 1, /* 2 */ + .bpp = 1, /* 2 bpp */ + .fetch_planes = MDSS_MDP_PLANE_PSEUDO_PLANAR, + .chroma_sample = MDSS_MDP_CHROMA_H2V1, + .element1 = C2_R_Cr, + .element0 = C1_B_Cb, + }, + [MDP_Y_CRCB_H1V2] = { + .format = MDP_Y_CRCB_H1V2, + .is_yuv = 1, + .a_bit = 0, + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 1, /* 2 */ + .bpp = 1, /* 2 bpp */ + .fetch_planes = MDSS_MDP_PLANE_PSEUDO_PLANAR, + .chroma_sample = MDSS_MDP_CHROMA_H1V2, + .element1 = C1_B_Cb, + .element0 = C2_R_Cr, + }, + [MDP_Y_CBCR_H1V2] = { + .format = MDP_Y_CBCR_H1V2, + .is_yuv = 1, + .a_bit = 0, + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 1, /* 2 */ + .bpp = 1, /* 2 bpp */ + .fetch_planes = MDSS_MDP_PLANE_PSEUDO_PLANAR, + .chroma_sample = MDSS_MDP_CHROMA_H1V2, + .element1 = C2_R_Cr, + .element0 = C1_B_Cb, + }, + [MDP_Y_CRCB_H2V2] = { + .format = MDP_Y_CRCB_H2V2, + .is_yuv = 1, + .a_bit = 0, + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 1, /* 2 */ + .bpp = 1, /* 2 bpp */ + .fetch_planes = MDSS_MDP_PLANE_PSEUDO_PLANAR, + .chroma_sample = MDSS_MDP_CHROMA_420, + .element1 = C1_B_Cb, + .element0 = C2_R_Cr, + }, + [MDP_Y_CBCR_H2V2] = { + .format = MDP_Y_CBCR_H2V2, + .is_yuv = 1, + .a_bit = 0, + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 1, /* 2 */ + .bpp = 1, /* 2 bpp */ + .fetch_planes = MDSS_MDP_PLANE_PSEUDO_PLANAR, + .chroma_sample = MDSS_MDP_CHROMA_420, + .element1 = C2_R_Cr, + .element0 = C1_B_Cb, + }, + [MDP_Y_CR_CB_H2V2] = { + .format = MDP_Y_CR_CB_H2V2, + .is_yuv = 1, + .a_bit = 0, + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 1, /* 2 */ + .bpp = 1, /* 2 bpp */ + .fetch_planes = MDSS_MDP_PLANE_PLANAR, + .chroma_sample = MDSS_MDP_CHROMA_420, + }, + [MDP_Y_CB_CR_H2V2] = { + .format = MDP_Y_CB_CR_H2V2, + .is_yuv = 1, + .a_bit = 0, + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 1, /* 2 */ + .bpp = 1, /* 2 bpp */ + .fetch_planes = MDSS_MDP_PLANE_PLANAR, + .chroma_sample = MDSS_MDP_CHROMA_420, + }, + [MDP_Y_CR_CB_GH2V2] = { + .format = MDP_Y_CR_CB_GH2V2, + .is_yuv = 1, + .a_bit = 0, + .r_bit = 3, /* R, 8 bits */ + .b_bit = 3, /* B, 8 bits */ + .g_bit = 3, /* G, 8 bits */ + .alpha_enable = 0, + .unpack_tight = 1, + .unpack_align_msb = 0, + .unpack_count = 1, /* 2 */ + .bpp = 1, /* 2 bpp */ + .fetch_planes = MDSS_MDP_PLANE_PLANAR, + .chroma_sample = MDSS_MDP_CHROMA_420, + }, +}; +#endif diff --git a/drivers/video/msm/mdss/mdss_mdp_hwio.h b/drivers/video/msm/mdss/mdss_mdp_hwio.h new file mode 100644 index 00000000000..4ca1dcefb42 --- /dev/null +++ b/drivers/video/msm/mdss/mdss_mdp_hwio.h @@ -0,0 +1,430 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef MDSS_MDP_HWIO_H +#define MDSS_MDP_HWIO_H + +#include + +#define MDSS_REG_HW_VERSION 0x0 +#define MDSS_REG_HW_INTR_STATUS 0x10 + +#define MDSS_INTR_MDP BIT(0) +#define MDSS_INTR_DSI0 BIT(4) +#define MDSS_INTR_DSI1 BIT(5) +#define MDSS_INTR_HDMI BIT(8) +#define MDSS_INTR_EDP BIT(12) + +#define MDSS_MDP_REG_HW_VERSION 0x00100 +#define MDSS_MDP_REG_DISP_INTF_SEL 0x00104 +#define MDSS_MDP_REG_INTR_EN 0x00110 +#define MDSS_MDP_REG_INTR_STATUS 0x00114 +#define MDSS_MDP_REG_INTR_CLEAR 0x00118 +#define MDSS_MDP_REG_HIST_INTR_EN 0x0011C +#define MDSS_MDP_REG_HIST_INTR_STATUS 0x00120 +#define MDSS_MDP_REG_HIST_INTR_CLEAR 0x00124 + +#define MDSS_INTF_DSI 0x1 +#define MDSS_INTF_HDMI 0x3 +#define MDSS_INTF_LCDC 0x5 +#define MDSS_INTF_EDP 0x9 + +#define MDSS_MDP_INTR_WB_0_DONE BIT(0) +#define MDSS_MDP_INTR_WB_1_DONE BIT(1) +#define MDSS_MDP_INTR_WB_2_DONE BIT(4) +#define MDSS_MDP_INTR_PING_PONG_0_DONE BIT(8) +#define MDSS_MDP_INTR_PING_PONG_1_DONE BIT(9) +#define MDSS_MDP_INTR_PING_PONG_2_DONE BIT(10) +#define MDSS_MDP_INTR_PING_PONG_0_RD_PTR BIT(12) +#define MDSS_MDP_INTR_PING_PONG_1_RD_PTR BIT(13) +#define MDSS_MDP_INTR_PING_PONG_2_RD_PTR BIT(14) +#define MDSS_MDP_INTR_PING_PONG_0_WR_PTR BIT(16) +#define MDSS_MDP_INTR_PING_PONG_1_WR_PTR BIT(17) +#define MDSS_MDP_INTR_PING_PONG_2_WR_PTR BIT(18) +#define MDSS_MDP_INTR_PING_PONG_0_AUTOREFRESH_DONE BIT(20) +#define MDSS_MDP_INTR_PING_PONG_1_AUTOREFRESH_DONE BIT(21) +#define MDSS_MDP_INTR_PING_PONG_2_AUTOREFRESH_DONE BIT(22) +#define MDSS_MDP_INTR_INTF_0_UNDERRUN BIT(24) +#define MDSS_MDP_INTR_INTF_0_VSYNC BIT(25) +#define MDSS_MDP_INTR_INTF_1_UNDERRUN BIT(26) +#define MDSS_MDP_INTR_INTF_1_VSYNC BIT(27) +#define MDSS_MDP_INTR_INTF_2_UNDERRUN BIT(28) +#define MDSS_MDP_INTR_INTF_2_VSYNC BIT(29) +#define MDSS_MDP_INTR_INTF_3_UNDERRUN BIT(30) +#define MDSS_MDP_INTR_INTF_3_VSYNC BIT(31) + +enum mdss_mdp_intr_type { + MDSS_MDP_IRQ_WB_ROT_COMP = 0, + MDSS_MDP_IRQ_WB_WFD = 4, + MDSS_MDP_IRQ_PING_PONG_COMP = 8, + MDSS_MDP_IRQ_PING_PONG_RD_PTR = 12, + MDSS_MDP_IRQ_PING_PONG_WR_PTR = 16, + MDSS_MDP_IRQ_PING_PONG_AUTO_REF = 20, + MDSS_MDP_IRQ_INTF_UNDER_RUN = 24, + MDSS_MDP_IRQ_INTF_VSYNC = 25, +}; + +enum mdss_mdp_ctl_index { + MDSS_MDP_CTL0, + MDSS_MDP_CTL1, + MDSS_MDP_CTL2, + MDSS_MDP_CTL3, + MDSS_MDP_CTL4, + MDSS_MDP_MAX_CTL +}; + +#define MDSS_MDP_REG_CTL_OFFSET(ctl) (0x00600 + ((ctl) * 0x100)) + +#define MDSS_MDP_REG_CTL_LAYER(lm) ((lm) * 0x004) +#define MDSS_MDP_REG_CTL_TOP 0x014 +#define MDSS_MDP_REG_CTL_FLUSH 0x018 +#define MDSS_MDP_REG_CTL_START 0x01C +#define MDSS_MDP_REG_CTL_PACK_3D 0x020 + +#define MDSS_MDP_CTL_OP_VIDEO_MODE (0 << 17) +#define MDSS_MDP_CTL_OP_CMD_MODE (1 << 17) + +#define MDSS_MDP_CTL_OP_ROT0_MODE 0x1 +#define MDSS_MDP_CTL_OP_ROT1_MODE 0x2 +#define MDSS_MDP_CTL_OP_WB0_MODE 0x3 +#define MDSS_MDP_CTL_OP_WB1_MODE 0x4 +#define MDSS_MDP_CTL_OP_WFD_MODE 0x5 + +#define MDSS_MDP_CTL_OP_PACK_3D_ENABLE BIT(19) +#define MDSS_MDP_CTL_OP_PACK_3D_FRAME_INT (0 << 20) +#define MDSS_MDP_CTL_OP_PACK_3D_H_ROW_INT (1 << 20) +#define MDSS_MDP_CTL_OP_PACK_3D_V_ROW_INT (2 << 20) +#define MDSS_MDP_CTL_OP_PACK_3D_COL_INT (3 << 20) + +enum mdss_mdp_sspp_index { + MDSS_MDP_SSPP_VIG0, + MDSS_MDP_SSPP_VIG1, + MDSS_MDP_SSPP_VIG2, + MDSS_MDP_SSPP_RGB0, + MDSS_MDP_SSPP_RGB1, + MDSS_MDP_SSPP_RGB2, + MDSS_MDP_SSPP_DMA0, + MDSS_MDP_SSPP_DMA1, + MDSS_MDP_MAX_SSPP +}; + +enum mdss_mdp_sspp_fetch_type { + MDSS_MDP_PLANE_INTERLEAVED, + MDSS_MDP_PLANE_PLANAR, + MDSS_MDP_PLANE_PSEUDO_PLANAR, +}; + +enum mdss_mdp_sspp_chroma_samp_type { + MDSS_MDP_CHROMA_RGB, + MDSS_MDP_CHROMA_H2V1, + MDSS_MDP_CHROMA_H1V2, + MDSS_MDP_CHROMA_420 +}; + +#define MDSS_MDP_REG_SSPP_OFFSET(pipe) (0x01200 + ((pipe) * 0x400)) + +#define MDSS_MDP_REG_SSPP_SRC_SIZE 0x000 +#define MDSS_MDP_REG_SSPP_SRC_IMG_SIZE 0x004 +#define MDSS_MDP_REG_SSPP_SRC_XY 0x008 +#define MDSS_MDP_REG_SSPP_OUT_SIZE 0x00C +#define MDSS_MDP_REG_SSPP_OUT_XY 0x010 +#define MDSS_MDP_REG_SSPP_SRC0_ADDR 0x014 +#define MDSS_MDP_REG_SSPP_SRC1_ADDR 0x018 +#define MDSS_MDP_REG_SSPP_SRC2_ADDR 0x01C +#define MDSS_MDP_REG_SSPP_SRC3_ADDR 0x020 +#define MDSS_MDP_REG_SSPP_SRC_YSTRIDE0 0x024 +#define MDSS_MDP_REG_SSPP_SRC_YSTRIDE1 0x028 +#define MDSS_MDP_REG_SSPP_STILE_FRAME_SIZE 0x02C +#define MDSS_MDP_REG_SSPP_SRC_FORMAT 0x030 +#define MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN 0x034 + +#define MDSS_MDP_REG_SSPP_SRC_OP_MODE 0x038 +#define MDSS_MDP_OP_DEINTERLACE BIT(22) +#define MDSS_MDP_OP_DEINTERLACE_ODD BIT(23) +#define MDSS_MDP_OP_FLIP_UD BIT(14) +#define MDSS_MDP_OP_FLIP_LR BIT(13) +#define MDSS_MDP_OP_BWC_EN BIT(0) +#define MDSS_MDP_OP_BWC_LOSSLESS (0 << 1) +#define MDSS_MDP_OP_BWC_Q_HIGH (1 << 1) +#define MDSS_MDP_OP_BWC_Q_MED (2 << 1) + +#define MDSS_MDP_REG_SSPP_SRC_CONSTANT_COLOR 0x03C +#define MDSS_MDP_REG_SSPP_FETCH_CONFIG 0x048 +#define MDSS_MDP_REG_SSPP_VC1_RANGE 0x04C + +#define MDSS_MDP_REG_SSPP_CURRENT_SRC0_ADDR 0x0A4 +#define MDSS_MDP_REG_SSPP_CURRENT_SRC1_ADDR 0x0A8 +#define MDSS_MDP_REG_SSPP_CURRENT_SRC2_ADDR 0x0AC +#define MDSS_MDP_REG_SSPP_CURRENT_SRC3_ADDR 0x0B0 +#define MDSS_MDP_REG_SSPP_LINE_SKIP_STEP_C03 0x0B4 +#define MDSS_MDP_REG_SSPP_LINE_SKIP_STEP_C12 0x0B8 + +#define MDSS_MDP_REG_VIG_OP_MODE 0x200 +#define MDSS_MDP_REG_VIG_QSEED2_CONFIG 0x204 +#define MDSS_MDP_REG_VIG_QSEED2_C03_PHASESTEPX 0x210 +#define MDSS_MDP_REG_VIG_QSEED2_C03_PHASESTEPY 0x214 +#define MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPX 0x218 +#define MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPY 0x21C +#define MDSS_MDP_REG_VIG_QSEED2_C03_INIT_PHASEX 0x220 +#define MDSS_MDP_REG_VIG_QSEED2_C03_INIT_PHASEY 0x224 +#define MDSS_MDP_REG_VIG_QSEED2_C12_INIT_PHASEX 0x228 +#define MDSS_MDP_REG_VIG_QSEED2_C12_INIT_PHASEY 0x22C + +#define MDSS_MDP_REG_SCALE_CONFIG 0x204 +#define MDSS_MDP_REG_SCALE_PHASE_STEP_X 0x210 +#define MDSS_MDP_REG_SCALE_PHASE_STEP_Y 0x214 +#define MDSS_MDP_REG_SCALE_INIT_PHASE_X 0x220 +#define MDSS_MDP_REG_SCALE_INIT_PHASE_Y 0x224 + +#define MDSS_MDP_REG_VIG_CSC_0_BASE 0x280 +#define MDSS_MDP_REG_VIG_CSC_1_BASE 0x320 + +#define MDSS_MDP_SCALE_FILTER_NEAREST 0x0 +#define MDSS_MDP_SCALE_FILTER_BIL 0x1 +#define MDSS_MDP_SCALE_FILTER_PCMN 0x2 +#define MDSS_MDP_SCALE_FILTER_CA 0x3 +#define MDSS_MDP_SCALEY_EN BIT(1) +#define MDSS_MDP_SCALEX_EN BIT(0) + +#define MDSS_MDP_NUM_REG_MIXERS 3 +#define MDSS_MDP_NUM_WB_MIXERS 2 + +enum mdss_mdp_mixer_index { + MDSS_MDP_LAYERMIXER0, + MDSS_MDP_LAYERMIXER1, + MDSS_MDP_LAYERMIXER2, + MDSS_MDP_LAYERMIXER3, + MDSS_MDP_LAYERMIXER4, + MDSS_MDP_MAX_LAYERMIXER +}; + +enum mdss_mdp_stage_index { + MDSS_MDP_STAGE_UNUSED, + MDSS_MDP_STAGE_BASE, + MDSS_MDP_STAGE_0, + MDSS_MDP_STAGE_1, + MDSS_MDP_STAGE_2, + MDSS_MDP_STAGE_3, + MDSS_MDP_MAX_STAGE +}; + +enum mdss_mdp_blend_index { + MDSS_MDP_BLEND_STAGE0, + MDSS_MDP_BLEND_STAGE1, + MDSS_MDP_BLEND_STAGE2, + MDSS_MDP_BLEND_STAGE3, + MDSS_MDP_MAX_BLEND_STAGE, +}; + +#define MDSS_MDP_REG_LM_OFFSET(lm) (0x03200 + ((lm) * 0x400)) + +#define MDSS_MDP_REG_LM_OP_MODE 0x000 +#define MDSS_MDP_REG_LM_OUT_SIZE 0x004 +#define MDSS_MDP_REG_LM_BORDER_COLOR_0 0x008 +#define MDSS_MDP_REG_LM_BORDER_COLOR_1 0x010 + +#define MDSS_MDP_REG_LM_BLEND_OFFSET(stage) (0x20 + ((stage) * 0x30)) +#define MDSS_MDP_REG_LM_BLEND_OP 0x00 +#define MDSS_MDP_REG_LM_BLEND_FG_ALPHA 0x04 +#define MDSS_MDP_REG_LM_BLEND_BG_ALPHA 0x08 +#define MDSS_MDP_REG_LM_BLEND_FG_TRANSP_LOW0 0x0C +#define MDSS_MDP_REG_LM_BLEND_FG_TRANSP_LOW1 0x10 +#define MDSS_MDP_REG_LM_BLEND_FG_TRANSP_HIGH0 0x14 +#define MDSS_MDP_REG_LM_BLEND_FG_TRANSP_HIGH1 0x18 +#define MDSS_MDP_REG_LM_BLEND_BG_TRANSP_LOW0 0x1C +#define MDSS_MDP_REG_LM_BLEND_BG_TRANSP_LOW1 0x20 +#define MDSS_MDP_REG_LM_BLEND_BG_TRANSP_HIGH0 0x24 +#define MDSS_MDP_REG_LM_BLEND_BG_TRANSP_HIGH1 0x28 + +#define MDSS_MDP_REG_LM_CURSOR_IMG_SIZE 0xE0 +#define MDSS_MDP_REG_LM_CURSOR_SIZE 0xE4 +#define MDSS_MDP_REG_LM_CURSOR_XY 0xE8 +#define MDSS_MDP_REG_LM_CURSOR_STRIDE 0xDC +#define MDSS_MDP_REG_LM_CURSOR_FORMAT 0xEC +#define MDSS_MDP_REG_LM_CURSOR_BASE_ADDR 0xF0 +#define MDSS_MDP_REG_LM_CURSOR_START_XY 0xF4 +#define MDSS_MDP_REG_LM_CURSOR_BLEND_CONFIG 0xF8 +#define MDSS_MDP_REG_LM_CURSOR_BLEND_PARAM 0xFC +#define MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_LOW0 0x100 +#define MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_LOW1 0x104 +#define MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_HIGH0 0x108 +#define MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_HIGH1 0x10C + +#define MDSS_MDP_LM_BORDER_COLOR (1 << 24) +#define MDSS_MDP_LM_CURSOR_OUT (1 << 25) +#define MDSS_MDP_BLEND_FG_ALPHA_FG_CONST (0 << 0) +#define MDSS_MDP_BLEND_FG_ALPHA_BG_CONST (1 << 0) +#define MDSS_MDP_BLEND_FG_ALPHA_FG_PIXEL (2 << 0) +#define MDSS_MDP_BLEND_FG_ALPHA_BG_PIXEL (3 << 0) +#define MDSS_MDP_BLEND_FG_INV_ALPHA (1 << 2) +#define MDSS_MDP_BLEND_FG_MOD_ALPHA (1 << 3) +#define MDSS_MDP_BLEND_FG_INV_MOD_ALPHA (1 << 4) +#define MDSS_MDP_BLEND_FG_TRANSP_EN (1 << 5) +#define MDSS_MDP_BLEND_BG_ALPHA_FG_CONST (0 << 8) +#define MDSS_MDP_BLEND_BG_ALPHA_BG_CONST (1 << 8) +#define MDSS_MDP_BLEND_BG_ALPHA_FG_PIXEL (2 << 8) +#define MDSS_MDP_BLEND_BG_ALPHA_BG_PIXEL (3 << 8) +#define MDSS_MDP_BLEND_BG_INV_ALPHA (1 << 10) +#define MDSS_MDP_BLEND_BG_MOD_ALPHA (1 << 11) +#define MDSS_MDP_BLEND_BG_INV_MOD_ALPHA (1 << 12) +#define MDSS_MDP_BLEND_BG_TRANSP_EN (1 << 13) + +enum mdss_mdp_writeback_index { + MDSS_MDP_WRITEBACK0, + MDSS_MDP_WRITEBACK1, + MDSS_MDP_WRITEBACK2, + MDSS_MDP_WRITEBACK3, + MDSS_MDP_WRITEBACK4, + MDSS_MDP_MAX_WRITEBACK +}; + +#define MDSS_MDP_REG_WB_OFFSET(wb) (0x11100 + ((wb) * 0x2000)) + +#define MDSS_MDP_REG_WB_DST_FORMAT 0x000 +#define MDSS_MDP_REG_WB_DST_OP_MODE 0x004 +#define MDSS_MDP_REG_WB_DST_PACK_PATTERN 0x008 +#define MDSS_MDP_REG_WB_DST0_ADDR 0x00C +#define MDSS_MDP_REG_WB_DST1_ADDR 0x010 +#define MDSS_MDP_REG_WB_DST2_ADDR 0x014 +#define MDSS_MDP_REG_WB_DST3_ADDR 0x018 +#define MDSS_MDP_REG_WB_DST_YSTRIDE0 0x01C +#define MDSS_MDP_REG_WB_DST_YSTRIDE1 0x020 +#define MDSS_MDP_REG_WB_DST_YSTRIDE1 0x020 +#define MDSS_MDP_REG_WB_DST_DITHER_BITDEPTH 0x024 +#define MDSS_MDP_REG_WB_DST_MATRIX_ROW0 0x030 +#define MDSS_MDP_REG_WB_DST_MATRIX_ROW1 0x034 +#define MDSS_MDP_REG_WB_DST_MATRIX_ROW2 0x038 +#define MDSS_MDP_REG_WB_DST_MATRIX_ROW3 0x03C +#define MDSS_MDP_REG_WB_ROTATION_DNSCALER 0x050 +#define MDSS_MDP_REG_WB_N16_INIT_PHASE_X_C03 0x060 +#define MDSS_MDP_REG_WB_N16_INIT_PHASE_X_C12 0x064 +#define MDSS_MDP_REG_WB_N16_INIT_PHASE_Y_C03 0x068 +#define MDSS_MDP_REG_WB_N16_INIT_PHASE_Y_C12 0x06C +#define MDSS_MDP_REG_WB_OUT_SIZE 0x074 +#define MDSS_MDP_REG_WB_ALPHA_X_VALUE 0x078 +#define MDSS_MDP_REG_WB_CSC_BASE 0x260 + +enum mdss_mdp_dspp_index { + MDSS_MDP_DSPP0, + MDSS_MDP_DSPP1, + MDSS_MDP_DSPP2, + MDSS_MDP_MAX_DSPP +}; + +enum mdss_mpd_intf_index { + MDSS_MDP_NO_INTF, + MDSS_MDP_INTF0, + MDSS_MDP_INTF1, + MDSS_MDP_INTF2, + MDSS_MDP_INTF3, + MDSS_MDP_MAX_INTF +}; + +#define MDSS_MDP_REG_INTF_OFFSET(intf) (0x20F00 + ((intf) * 0x200)) + +#define MDSS_MDP_REG_INTF_TIMING_ENGINE_EN 0x000 +#define MDSS_MDP_REG_INTF_CONFIG 0x004 +#define MDSS_MDP_REG_INTF_HSYNC_CTL 0x008 +#define MDSS_MDP_REG_INTF_VSYNC_PERIOD_F0 0x00C +#define MDSS_MDP_REG_INTF_VSYNC_PERIOD_F1 0x010 +#define MDSS_MDP_REG_INTF_VSYNC_PULSE_WIDTH_F0 0x014 +#define MDSS_MDP_REG_INTF_VSYNC_PULSE_WIDTH_F1 0x018 +#define MDSS_MDP_REG_INTF_DISPLAY_V_START_F0 0x01C +#define MDSS_MDP_REG_INTF_DISPLAY_V_START_F1 0x020 +#define MDSS_MDP_REG_INTF_DISPLAY_V_END_F0 0x024 +#define MDSS_MDP_REG_INTF_DISPLAY_V_END_F1 0x028 +#define MDSS_MDP_REG_INTF_ACTIVE_V_START_F0 0x02C +#define MDSS_MDP_REG_INTF_ACTIVE_V_START_F1 0x030 +#define MDSS_MDP_REG_INTF_ACTIVE_V_END_F0 0x034 +#define MDSS_MDP_REG_INTF_ACTIVE_V_END_F1 0x038 +#define MDSS_MDP_REG_INTF_DISPLAY_HCTL 0x03C +#define MDSS_MDP_REG_INTF_ACTIVE_HCTL 0x040 +#define MDSS_MDP_REG_INTF_BORDER_COLOR 0x044 +#define MDSS_MDP_REG_INTF_UNDERFLOW_COLOR 0x048 +#define MDSS_MDP_REG_INTF_HSYNC_SKEW 0x04C +#define MDSS_MDP_REG_INTF_POLARITY_CTL 0x050 +#define MDSS_MDP_REG_INTF_TEST_CTL 0x054 +#define MDSS_MDP_REG_INTF_TP_COLOR0 0x058 +#define MDSS_MDP_REG_INTF_TP_COLOR1 0x05C + +#define MDSS_MDP_REG_INTF_DEFLICKER_CONFIG 0x0F0 +#define MDSS_MDP_REG_INTF_DEFLICKER_STRNG_COEFF 0x0F4 +#define MDSS_MDP_REG_INTF_DEFLICKER_WEAK_COEFF 0x0F8 + +#define MDSS_MDP_REG_INTF_DSI_CMD_MODE_TRIGGER_EN 0x084 +#define MDSS_MDP_REG_INTF_PANEL_FORMAT 0x090 +#define MDSS_MDP_REG_INTF_TPG_ENABLE 0x100 +#define MDSS_MDP_REG_INTF_TPG_MAIN_CONTROL 0x104 +#define MDSS_MDP_REG_INTF_TPG_VIDEO_CONFIG 0x108 +#define MDSS_MDP_REG_INTF_TPG_COMPONENT_LIMITS 0x10C +#define MDSS_MDP_REG_INTF_TPG_RECTANGLE 0x110 +#define MDSS_MDP_REG_INTF_TPG_INITIAL_VALUE 0x114 +#define MDSS_MDP_REG_INTF_TPG_BLK_WHITE_PATTERN_FRAMES 0x118 +#define MDSS_MDP_REG_INTF_TPG_RGB_MAPPING 0x11C + +#define MDSS_MDP_REG_INTF_FRAME_LINE_COUNT_EN 0x0A8 +#define MDSS_MDP_REG_INTF_FRAME_COUNT 0x0AC +#define MDSS_MDP_REG_INTF_LINE_COUNT 0x0B0 + +enum mdss_mdp_pingpong_index { + MDSS_MDP_PINGPONG0, + MDSS_MDP_PINGPONG1, + MDSS_MDP_PINGPONG2, + MDSS_MDP_MAX_PINGPONG +}; + +#define MDSS_MDP_REG_PP_OFFSET(pp) (0x21B00 + ((pp) * 0x100)) + +#define MDSS_MDP_REG_PP_TEAR_CHECK_EN 0x000 +#define MDSS_MDP_REG_PP_SYNC_CONFIG_VSYNC 0x004 +#define MDSS_MDP_REG_PP_SYNC_CONFIG_HEIGHT 0x008 +#define MDSS_MDP_REG_PP_SYNC_WRCOUNT 0x00C +#define MDSS_MDP_REG_PP_VSYNC_INIT_VAL 0x010 +#define MDSS_MDP_REG_PP_INT_COUNT_VAL 0x014 +#define MDSS_MDP_REG_PP_SYNC_THRESH 0x018 +#define MDSS_MDP_REG_PP_START_POS 0x01C +#define MDSS_MDP_REG_PP_RD_PTR_IRQ 0x020 +#define MDSS_MDP_REG_PP_WR_PTR_IRQ 0x024 +#define MDSS_MDP_REG_PP_OUT_LINE_COUNT 0x028 +#define MDSS_MDP_REG_PP_LINE_COUNT 0x02C +#define MDSS_MDP_REG_PP_AUTOREFRESH_CONFIG 0x030 + +#define MDSS_MDP_REG_SMP_ALLOC_W0 0x00180 +#define MDSS_MDP_REG_SMP_ALLOC_R0 0x00230 + +#define MDSS_MDP_SMP_MMB_SIZE 4096 +#define MDSS_MDP_SMP_MMB_BLOCKS 22 + +enum mdss_mdp_smp_client_index { + MDSS_MDP_SMP_CLIENT_UNUSED, + MDSS_MDP_SMP_CLIENT_VIG0_FETCH_Y, + MDSS_MDP_SMP_CLIENT_VIG0_FETCH_CR, + MDSS_MDP_SMP_CLIENT_VIG0_FETCH_CB, + MDSS_MDP_SMP_CLIENT_VIG1_FETCH_Y, + MDSS_MDP_SMP_CLIENT_VIG1_FETCH_CR, + MDSS_MDP_SMP_CLIENT_VIG1_FETCH_CB, + MDSS_MDP_SMP_CLIENT_VIG2_FETCH_Y, + MDSS_MDP_SMP_CLIENT_VIG2_FETCH_CR, + MDSS_MDP_SMP_CLIENT_VIG2_FETCH_CB, + MDSS_MDP_SMP_CLIENT_DMA0_FETCH_Y, + MDSS_MDP_SMP_CLIENT_DMA0_FETCH_CR, + MDSS_MDP_SMP_CLIENT_DMA0_FETCH_CB, + MDSS_MDP_SMP_CLIENT_DMA1_FETCH_Y, + MDSS_MDP_SMP_CLIENT_DMA1_FETCH_CR, + MDSS_MDP_SMP_CLIENT_DMA1_FETCH_CB, + MDSS_MDP_SMP_CLIENT_RGB0_FETCH, + MDSS_MDP_SMP_CLIENT_RGB1_FETCH, + MDSS_MDP_SMP_CLIENT_RGB2_FETCH, +}; + +#endif /* MDSS_MDP_HWIO_H */ diff --git a/drivers/video/msm/mdss/mdss_mdp_overlay.c b/drivers/video/msm/mdss/mdss_mdp_overlay.c new file mode 100644 index 00000000000..517f8f57c2a --- /dev/null +++ b/drivers/video/msm/mdss/mdss_mdp_overlay.c @@ -0,0 +1,465 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include + +#include "mdss_fb.h" +#include "mdss_mdp.h" + +#define CHECK_BOUNDS(offset, size, max_size) \ + (((size) > (max_size)) || ((offset) > ((max_size) - (size)))) + +static int mdss_mdp_overlay_req_check(struct msm_fb_data_type *mfd, + struct mdp_overlay *req, + struct mdss_mdp_format_params *fmt) +{ + u32 xres, yres; + u32 dst_w, dst_h; + + xres = mfd->fbi->var.xres; + yres = mfd->fbi->var.yres; + + if (req->z_order >= MDSS_MDP_MAX_STAGE) { + pr_err("zorder %d out of range\n", req->z_order); + return -ERANGE; + } + + if (req->src.width > MAX_IMG_WIDTH || + req->src.height > MAX_IMG_HEIGHT || + req->src_rect.w == 0 || req->src_rect.h == 0 || + req->dst_rect.w < MIN_DST_W || req->dst_rect.h < MIN_DST_H || + req->dst_rect.w > MAX_DST_W || req->dst_rect.h > MAX_DST_H || + CHECK_BOUNDS(req->src_rect.x, req->src_rect.w, req->src.width) || + CHECK_BOUNDS(req->src_rect.y, req->src_rect.h, req->src.height) || + CHECK_BOUNDS(req->dst_rect.x, req->dst_rect.w, xres) || + CHECK_BOUNDS(req->dst_rect.y, req->dst_rect.h, yres)) { + pr_err("invalid image img_w=%d img_h=%d\n", + req->src.width, req->src.height); + + pr_err("\tsrc_rect=%d,%d,%d,%d dst_rect=%d,%d,%d,%d\n", + req->src_rect.x, req->src_rect.y, + req->src_rect.w, req->src_rect.h, + req->dst_rect.x, req->dst_rect.y, + req->dst_rect.w, req->dst_rect.h); + return -EINVAL; + } + + if (req->flags & MDP_ROT_90) { + dst_h = req->dst_rect.w; + dst_w = req->dst_rect.h; + } else { + dst_w = req->dst_rect.w; + dst_h = req->dst_rect.h; + } + + if ((req->src_rect.w * MAX_UPSCALE_RATIO) < dst_w) { + pr_err("too much upscaling Width %d->%d\n", + req->src_rect.w, req->dst_rect.w); + return -EINVAL; + } + + if ((req->src_rect.h * MAX_UPSCALE_RATIO) < dst_h) { + pr_err("too much upscaling. Height %d->%d\n", + req->src_rect.h, req->dst_rect.h); + return -EINVAL; + } + + if (req->src_rect.w > (dst_w * MAX_DOWNSCALE_RATIO)) { + pr_err("too much downscaling. Width %d->%d\n", + req->src_rect.w, req->dst_rect.w); + return -EINVAL; + } + + if (req->src_rect.h > (dst_h * MAX_DOWNSCALE_RATIO)) { + pr_err("too much downscaling. Height %d->%d\n", + req->src_rect.h, req->dst_rect.h); + return -EINVAL; + } + + if (fmt->is_yuv) { + if ((req->src_rect.x & 0x1) || (req->src_rect.y & 0x1) || + (req->src_rect.w & 0x1) || (req->src_rect.h & 0x1)) { + pr_err("invalid odd src resolution\n"); + return -EINVAL; + } + if ((req->dst_rect.x & 0x1) || (req->dst_rect.y & 0x1) || + (req->dst_rect.w & 0x1) || (req->dst_rect.h & 0x1)) { + pr_err("invalid odd dst resolution\n"); + return -EINVAL; + } + + if (((req->src_rect.w * (MAX_UPSCALE_RATIO / 2)) < dst_w) && + (fmt->chroma_sample == MDSS_MDP_CHROMA_420 || + fmt->chroma_sample == MDSS_MDP_CHROMA_H2V1)) { + pr_err("too much YUV upscaling Width %d->%d\n", + req->src_rect.w, req->dst_rect.w); + return -EINVAL; + } + + if (((req->src_rect.h * (MAX_UPSCALE_RATIO / 2)) < dst_h) && + (fmt->chroma_sample == MDSS_MDP_CHROMA_420 || + fmt->chroma_sample == MDSS_MDP_CHROMA_H1V2)) { + pr_err("too much YUV upscaling Height %d->%d\n", + req->src_rect.h, req->dst_rect.h); + return -EINVAL; + } + } + + return 0; +} + +static int mdss_mdp_overlay_pipe_setup(struct msm_fb_data_type *mfd, + struct mdp_overlay *req, + struct mdss_mdp_pipe **ppipe) +{ + struct mdss_mdp_format_params *fmt; + struct mdss_mdp_pipe *pipe; + struct mdss_mdp_mixer *mixer = NULL; + u32 pipe_type, mixer_mux; + int ret; + + if (mfd == NULL || mfd->ctl == NULL) + return -ENODEV; + + if (req->flags & MDSS_MDP_RIGHT_MIXER) + mixer_mux = MDSS_MDP_MIXER_MUX_RIGHT; + else + mixer_mux = MDSS_MDP_MIXER_MUX_LEFT; + + pr_debug("pipe ctl=%u req id=%x mux=%d\n", mfd->ctl->num, req->id, + mixer_mux); + + if (req->flags & MDP_ROT_90) { + pr_err("unsupported inline rotation\n"); + return -ENOTSUPP; + } + + fmt = mdss_mdp_get_format_params(req->src.format); + if (!fmt) { + pr_err("invalid pipe format %d\n", req->src.format); + return -EINVAL; + } + + ret = mdss_mdp_overlay_req_check(mfd, req, fmt); + if (ret) + return ret; + + pipe = mdss_mdp_mixer_stage_pipe(mfd->ctl, mixer_mux, req->z_order); + if (pipe && pipe->ndx != req->id) { + pr_err("stage %d taken by pnum=%d\n", req->z_order, pipe->num); + return -EBUSY; + } + + + if (req->id == MSMFB_NEW_REQUEST) { + mixer = mdss_mdp_mixer_get(mfd->ctl, mixer_mux); + if (!mixer) { + pr_err("unable to get mixer\n"); + return -ENODEV; + } + + if (fmt->is_yuv || (req->flags & MDP_OV_PIPE_SHARE)) + pipe_type = MDSS_MDP_PIPE_TYPE_VIG; + else + pipe_type = MDSS_MDP_PIPE_TYPE_RGB; + + pipe = mdss_mdp_pipe_alloc_locked(pipe_type); + + /* VIG pipes can also support RGB format */ + if (!pipe && pipe_type == MDSS_MDP_PIPE_TYPE_RGB) { + pipe_type = MDSS_MDP_PIPE_TYPE_VIG; + pipe = mdss_mdp_pipe_alloc_locked(pipe_type); + } + + if (pipe == NULL) { + pr_err("error allocating pipe\n"); + return -ENOMEM; + } + + pipe->mixer = mixer; + pipe->mfd = mfd; + } else { + pipe = mdss_mdp_pipe_get_locked(req->id); + if (pipe == NULL) { + pr_err("invalid pipe ndx=%x\n", req->id); + return -ENODEV; + } + } + + pipe->flags = req->flags; + + pipe->img_width = req->src.width & 0x3fff; + pipe->img_height = req->src.height & 0x3fff; + pipe->src.x = req->src_rect.x; + pipe->src.y = req->src_rect.y; + pipe->src.w = req->src_rect.w; + pipe->src.h = req->src_rect.h; + pipe->dst.x = req->dst_rect.x; + pipe->dst.y = req->dst_rect.y; + pipe->dst.w = req->dst_rect.w; + pipe->dst.h = req->dst_rect.h; + + pipe->src_fmt = fmt; + + pipe->mixer_stage = req->z_order; + pipe->is_fg = req->is_fg; + pipe->alpha = req->alpha; + pipe->transp = req->transp_mask; + + pipe->req_data = *req; + + pipe->params_changed++; + + req->id = pipe->ndx; + + *ppipe = pipe; + + mdss_mdp_pipe_unlock(pipe); + + return ret; +} + +static int mdss_mdp_overlay_get_fb_pipe(struct msm_fb_data_type *mfd, + struct mdss_mdp_pipe **ppipe, + int mixer_mux) +{ + struct mdss_mdp_pipe *pipe; + + pipe = mdss_mdp_mixer_stage_pipe(mfd->ctl, mixer_mux, + MDSS_MDP_STAGE_BASE); + if (pipe == NULL) { + struct mdp_overlay req; + int ret; + + memset(&req, 0, sizeof(req)); + + req.id = MSMFB_NEW_REQUEST; + req.src.format = mfd->fb_imgType; + req.src.height = mfd->fbi->var.yres; + req.src.width = mfd->fbi->var.xres; + if (mixer_mux == MDSS_MDP_MIXER_MUX_RIGHT) { + if (req.src.width <= MAX_MIXER_WIDTH) + return -ENODEV; + + req.flags |= MDSS_MDP_RIGHT_MIXER; + req.src_rect.x = MAX_MIXER_WIDTH; + req.src_rect.w = req.src.width - MAX_MIXER_WIDTH; + } else { + req.src_rect.x = 0; + req.src_rect.w = MIN(req.src.width, MAX_MIXER_WIDTH); + } + + req.src_rect.y = 0; + req.src_rect.h = req.src.height; + req.dst_rect.x = req.src_rect.x; + req.dst_rect.y = 0; + req.dst_rect.w = req.src_rect.w; + req.dst_rect.h = req.src_rect.h; + req.z_order = MDSS_MDP_STAGE_BASE; + + pr_debug("allocating base pipe mux=%d\n", mixer_mux); + + ret = mdss_mdp_overlay_pipe_setup(mfd, &req, &pipe); + if (ret) + return ret; + + pr_debug("ctl=%d pnum=%d\n", mfd->ctl->num, pipe->num); + } + + *ppipe = pipe; + return 0; +} + +static void mdss_mdp_overlay_pan_display(struct msm_fb_data_type *mfd) +{ + struct mdss_mdp_data data; + struct mdss_mdp_pipe *pipe; + struct fb_info *fbi; + u32 offset; + int bpp, ret; + + if (!mfd) + return; + + if (!mfd->ctl || !mfd->panel_power_on) + return; + + fbi = mfd->fbi; + + if (fbi->fix.smem_len == 0) { + pr_warn("fb memory not allocated\n"); + return; + } + + memset(&data, 0, sizeof(data)); + + bpp = fbi->var.bits_per_pixel / 8; + offset = fbi->var.xoffset * bpp + + fbi->var.yoffset * fbi->fix.line_length; + + data.p[0].addr = fbi->fix.smem_start + offset; + data.p[0].len = fbi->fix.smem_len - offset; + data.num_planes = 1; + + ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe, MDSS_MDP_MIXER_MUX_LEFT); + if (ret) { + pr_err("unable to allocate base pipe\n"); + return; + } + + mdss_mdp_pipe_lock(pipe); + ret = mdss_mdp_pipe_queue_data(pipe, &data); + mdss_mdp_pipe_unlock(pipe); + if (ret) { + pr_err("unable to queue data\n"); + return; + } + + if (fbi->var.xres > MAX_MIXER_WIDTH) { + ret = mdss_mdp_overlay_get_fb_pipe(mfd, &pipe, + MDSS_MDP_MIXER_MUX_RIGHT); + if (ret) { + pr_err("unable to allocate right base pipe\n"); + return; + } + mdss_mdp_pipe_lock(pipe); + ret = mdss_mdp_pipe_queue_data(pipe, &data); + mdss_mdp_pipe_unlock(pipe); + if (ret) { + pr_err("unable to queue right data\n"); + return; + } + } + + mdss_mdp_display_commit(mfd->ctl, NULL); +} + +static int mdss_mdp_hw_cursor_update(struct fb_info *info, + struct fb_cursor *cursor) +{ + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)info->par; + struct mdss_mdp_mixer *mixer; + struct fb_image *img = &cursor->image; + int calpha_en, transp_en, blendcfg, alpha; + int off, ret = 0; + + mixer = mdss_mdp_mixer_get(mfd->ctl, MDSS_MDP_MIXER_MUX_DEFAULT); + off = MDSS_MDP_REG_LM_OFFSET(mixer->num); + + if ((img->width > MDSS_MDP_CURSOR_WIDTH) || + (img->height > MDSS_MDP_CURSOR_HEIGHT) || + (img->depth != 32)) + return -EINVAL; + + pr_debug("mixer=%d enable=%x set=%x\n", mixer->num, cursor->enable, + cursor->set); + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + blendcfg = MDSS_MDP_REG_READ(off + MDSS_MDP_REG_LM_CURSOR_BLEND_CONFIG); + + if (cursor->set & FB_CUR_SETPOS) + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_START_XY, + (img->dy << 16) | img->dx); + + if (cursor->set & FB_CUR_SETIMAGE) { + ret = copy_from_user(mfd->cursor_buf, img->data, + img->width * img->height * 4); + if (ret) + return ret; + + if (img->bg_color == 0xffffffff) + transp_en = 0; + else + transp_en = 1; + + alpha = (img->fg_color & 0xff000000) >> 24; + + if (alpha) + calpha_en = 0x0; /* xrgb */ + else + calpha_en = 0x2; /* argb */ + + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_SIZE, + (img->height << 16) | img->width); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_STRIDE, + img->width * 4); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_BASE_ADDR, + mfd->cursor_buf_phys); + + wmb(); + + blendcfg &= ~0x1; + blendcfg |= (transp_en << 3) | (calpha_en << 1); + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_BLEND_CONFIG, + blendcfg); + if (calpha_en) + MDSS_MDP_REG_WRITE(off + + MDSS_MDP_REG_LM_CURSOR_BLEND_PARAM, + alpha); + + if (transp_en) { + MDSS_MDP_REG_WRITE(off + + MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_LOW0, + ((img->bg_color & 0xff00) << 8) | + (img->bg_color & 0xff)); + MDSS_MDP_REG_WRITE(off + + MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_LOW1, + ((img->bg_color & 0xff0000) >> 16)); + MDSS_MDP_REG_WRITE(off + + MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_HIGH0, + ((img->bg_color & 0xff00) << 8) | + (img->bg_color & 0xff)); + MDSS_MDP_REG_WRITE(off + + MDSS_MDP_REG_LM_CURSOR_BLEND_TRANSP_HIGH1, + ((img->bg_color & 0xff0000) >> 16)); + } + } + + if (!cursor->enable != !(blendcfg & 0x1)) { + if (cursor->enable) + blendcfg |= 0x1; + else + blendcfg &= ~0x1; + + MDSS_MDP_REG_WRITE(off + MDSS_MDP_REG_LM_CURSOR_BLEND_CONFIG, + blendcfg); + + mixer->cursor_enabled = cursor->enable; + mixer->params_changed++; + } + + mixer->ctl->flush_bits |= BIT(6) << mixer->num; + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + + return 0; +} + +int mdss_mdp_overlay_init(struct msm_fb_data_type *mfd) +{ + mfd->on_fnc = mdss_mdp_ctl_on; + mfd->off_fnc = mdss_mdp_ctl_off; + mfd->hw_refresh = true; + mfd->lut_update = NULL; + mfd->do_histogram = NULL; + mfd->overlay_play_enable = false; + mfd->cursor_update = mdss_mdp_hw_cursor_update; + mfd->dma_fnc = mdss_mdp_overlay_pan_display; + + return 0; +} diff --git a/drivers/video/msm/mdss/mdss_mdp_pipe.c b/drivers/video/msm/mdss/mdss_mdp_pipe.c new file mode 100644 index 00000000000..b52cff57ab6 --- /dev/null +++ b/drivers/video/msm/mdss/mdss_mdp_pipe.c @@ -0,0 +1,675 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include + +#include "mdss_mdp.h" + +#define SMP_MB_CNT (mdss_res->smp_mb_cnt) + +static DEFINE_MUTEX(mdss_mdp_sspp_lock); +static DECLARE_BITMAP(mdss_mdp_smp_mmb_pool, MDSS_MDP_SMP_MMB_BLOCKS); + +static struct mdss_mdp_pipe mdss_mdp_pipe_list[MDSS_MDP_MAX_SSPP]; + +static u32 mdss_mdp_smp_mmb_reserve(unsigned long *smp, size_t n) +{ + u32 i, mmb; + + /* reserve more blocks if needed, but can't free mmb at this point */ + for (i = bitmap_weight(smp, SMP_MB_CNT); i < n; i++) { + if (bitmap_full(mdss_mdp_smp_mmb_pool, SMP_MB_CNT)) + break; + + mmb = find_first_zero_bit(mdss_mdp_smp_mmb_pool, SMP_MB_CNT); + set_bit(mmb, smp); + set_bit(mmb, mdss_mdp_smp_mmb_pool); + } + return i; +} + +static void mdss_mdp_smp_mmb_set(int client_id, unsigned long *smp) +{ + u32 mmb, off, data, s; + + for_each_set_bit(mmb, smp, SMP_MB_CNT) { + off = (mmb / 3) * 4; + s = (mmb % 3) * 8; + data = MDSS_MDP_REG_READ(MDSS_MDP_REG_SMP_ALLOC_W0 + off); + data &= ~(0xFF << s); + data |= client_id << s; + MDSS_MDP_REG_WRITE(MDSS_MDP_REG_SMP_ALLOC_W0 + off, data); + MDSS_MDP_REG_WRITE(MDSS_MDP_REG_SMP_ALLOC_R0 + off, data); + } +} + +static void mdss_mdp_smp_mmb_free(unsigned long *smp) +{ + if (!bitmap_empty(smp, SMP_MB_CNT)) { + mdss_mdp_smp_mmb_set(MDSS_MDP_SMP_CLIENT_UNUSED, smp); + bitmap_andnot(mdss_mdp_smp_mmb_pool, mdss_mdp_smp_mmb_pool, + smp, SMP_MB_CNT); + bitmap_zero(smp, SMP_MB_CNT); + } +} + +static void mdss_mdp_smp_free(struct mdss_mdp_pipe *pipe) +{ + mdss_mdp_smp_mmb_free(&pipe->smp[0]); + mdss_mdp_smp_mmb_free(&pipe->smp[1]); + mdss_mdp_smp_mmb_free(&pipe->smp[2]); +} + +static int mdss_mdp_smp_reserve(struct mdss_mdp_pipe *pipe) +{ + u32 num_blks = 0, reserved = 0; + int i; + + if ((pipe->src_planes.num_planes > 1) && + (pipe->type == MDSS_MDP_PIPE_TYPE_RGB)) + return -EINVAL; + + mutex_lock(&mdss_mdp_sspp_lock); + for (i = 0; i < pipe->src_planes.num_planes; i++) { + num_blks = DIV_ROUND_UP(2 * pipe->src_planes.ystride[i], + mdss_res->smp_mb_size); + + pr_debug("reserving %d mmb for pnum=%d plane=%d\n", + num_blks, pipe->num, i); + reserved = mdss_mdp_smp_mmb_reserve(&pipe->smp[i], num_blks); + + if (reserved < num_blks) + break; + } + + if (reserved < num_blks) { + pr_err("insufficient MMB blocks\n"); + mdss_mdp_smp_free(pipe); + return -ENOMEM; + } + mutex_unlock(&mdss_mdp_sspp_lock); + + return 0; +} + +static int mdss_mdp_smp_alloc(struct mdss_mdp_pipe *pipe) +{ + u32 client_id; + int i; + + switch (pipe->num) { + case MDSS_MDP_SSPP_VIG0: + client_id = MDSS_MDP_SMP_CLIENT_VIG0_FETCH_Y; + break; + case MDSS_MDP_SSPP_VIG1: + client_id = MDSS_MDP_SMP_CLIENT_VIG1_FETCH_Y; + break; + case MDSS_MDP_SSPP_VIG2: + client_id = MDSS_MDP_SMP_CLIENT_VIG2_FETCH_Y; + break; + case MDSS_MDP_SSPP_RGB0: + client_id = MDSS_MDP_SMP_CLIENT_RGB0_FETCH; + break; + case MDSS_MDP_SSPP_RGB1: + client_id = MDSS_MDP_SMP_CLIENT_RGB1_FETCH; + break; + case MDSS_MDP_SSPP_RGB2: + client_id = MDSS_MDP_SMP_CLIENT_RGB2_FETCH; + break; + case MDSS_MDP_SSPP_DMA0: + client_id = MDSS_MDP_SMP_CLIENT_DMA0_FETCH_Y; + break; + case MDSS_MDP_SSPP_DMA1: + client_id = MDSS_MDP_SMP_CLIENT_DMA1_FETCH_Y; + break; + default: + pr_err("no valid smp client for pnum=%d\n", pipe->num); + return -EINVAL; + } + + mutex_lock(&mdss_mdp_sspp_lock); + for (i = 0; i < pipe->src_planes.num_planes; i++) + mdss_mdp_smp_mmb_set(client_id + i, &pipe->smp[i]); + mutex_unlock(&mdss_mdp_sspp_lock); + return 0; +} + +void mdss_mdp_pipe_unlock(struct mdss_mdp_pipe *pipe) +{ + atomic_dec(&pipe->ref_cnt); + mutex_unlock(&pipe->lock); +} + +int mdss_mdp_pipe_lock(struct mdss_mdp_pipe *pipe) +{ + if (atomic_inc_not_zero(&pipe->ref_cnt)) { + if (mutex_lock_interruptible(&pipe->lock)) { + atomic_dec(&pipe->ref_cnt); + return -EINTR; + } + return 0; + } + return -EINVAL; +} + +static struct mdss_mdp_pipe *mdss_mdp_pipe_init(u32 pnum) +{ + struct mdss_mdp_pipe *pipe = NULL; + + if (atomic_read(&mdss_mdp_pipe_list[pnum].ref_cnt) == 0) { + pipe = &mdss_mdp_pipe_list[pnum]; + memset(pipe, 0, sizeof(*pipe)); + + mutex_init(&pipe->lock); + atomic_set(&pipe->ref_cnt, 1); + + if (mdss_mdp_pipe_lock(pipe) == 0) { + pipe->num = pnum; + pipe->type = mdss_res->pipe_type_map[pnum]; + pipe->ndx = BIT(pnum); + + pr_debug("ndx=%x pnum=%d\n", pipe->ndx, pipe->num); + } else { + atomic_set(&pipe->ref_cnt, 0); + pipe = NULL; + } + } + return pipe; +} + +struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_pnum(u32 pnum) +{ + struct mdss_mdp_pipe *pipe = NULL; + mutex_lock(&mdss_mdp_sspp_lock); + if (mdss_res->pipe_type_map[pnum] != MDSS_MDP_PIPE_TYPE_UNUSED) + pipe = mdss_mdp_pipe_init(pnum); + mutex_unlock(&mdss_mdp_sspp_lock); + return pipe; +} + +struct mdss_mdp_pipe *mdss_mdp_pipe_alloc_locked(u32 type) +{ + struct mdss_mdp_pipe *pipe = NULL; + int pnum; + + mutex_lock(&mdss_mdp_sspp_lock); + for (pnum = 0; pnum < MDSS_MDP_MAX_SSPP; pnum++) { + if (type == mdss_res->pipe_type_map[pnum]) { + pipe = mdss_mdp_pipe_init(pnum); + if (pipe) + break; + } + } + mutex_unlock(&mdss_mdp_sspp_lock); + + return pipe; +} + +struct mdss_mdp_pipe *mdss_mdp_pipe_get_locked(u32 ndx) +{ + struct mdss_mdp_pipe *pipe = NULL; + int i; + + if (!ndx) + return NULL; + + mutex_lock(&mdss_mdp_sspp_lock); + for (i = 0; i < MDSS_MDP_MAX_SSPP; i++) { + pipe = &mdss_mdp_pipe_list[i]; + if (ndx == pipe->ndx) + break; + } + mutex_unlock(&mdss_mdp_sspp_lock); + + if (i == MDSS_MDP_MAX_SSPP) + return NULL; + + if (mdss_mdp_pipe_lock(pipe)) + return NULL; + + if (pipe->ndx != ndx) { + mdss_mdp_pipe_unlock(pipe); + pipe = NULL; + } + + return pipe; +} + + +static void mdss_mdp_pipe_free(struct mdss_mdp_pipe *pipe) +{ + mdss_mdp_smp_free(pipe); + pipe->ndx = 0; + atomic_dec(&pipe->ref_cnt); + mdss_mdp_pipe_unlock(pipe); +} + +int mdss_mdp_pipe_destroy(struct mdss_mdp_pipe *pipe) +{ + pr_debug("ndx=%x pnum=%d ref_cnt=%d\n", pipe->ndx, pipe->num, + atomic_read(&pipe->ref_cnt)); + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + mutex_lock(&mdss_mdp_sspp_lock); + mdss_mdp_pipe_free(pipe); + mutex_unlock(&mdss_mdp_sspp_lock); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + + return 0; +} + +int mdss_mdp_pipe_release_all(struct msm_fb_data_type *mfd) +{ + struct mdss_mdp_pipe *pipe; + int i; + + if (!mfd) + return -ENODEV; + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + mutex_lock(&mdss_mdp_sspp_lock); + for (i = 0; i < MDSS_MDP_MAX_SSPP; i++) { + pipe = &mdss_mdp_pipe_list[i]; + if (atomic_read(&pipe->ref_cnt) && pipe->mfd == mfd) { + pr_debug("release pnum=%d\n", pipe->num); + if (mdss_mdp_pipe_lock(pipe) == 0) { + mdss_mdp_mixer_pipe_unstage(pipe); + mdss_mdp_pipe_free(pipe); + } else { + pr_err("unable to lock pipe=%d for release", + pipe->num); + } + } + } + mutex_unlock(&mdss_mdp_sspp_lock); + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + + return 0; +} + +static inline void mdss_mdp_pipe_write(struct mdss_mdp_pipe *pipe, + u32 reg, u32 val) +{ + int offset = MDSS_MDP_REG_SSPP_OFFSET(pipe->num); + MDSS_MDP_REG_WRITE(offset + reg, val); +} + +static inline u32 mdss_mdp_pipe_read(struct mdss_mdp_pipe *pipe, u32 reg) +{ + int offset = MDSS_MDP_REG_SSPP_OFFSET(pipe->num); + return MDSS_MDP_REG_READ(offset + reg); +} + +static int mdss_mdp_leading_zero(u32 num) +{ + u32 bit = 0x80000000; + int i; + + for (i = 0; i < 32; i++) { + if (bit & num) + return i; + bit >>= 1; + } + + return i; +} + +static u32 mdss_mdp_scale_phase_step(int f_num, u32 src, u32 dst) +{ + u32 val, s; + int n; + + n = mdss_mdp_leading_zero(src); + if (n > f_num) + n = f_num; + s = src << n; /* maximum to reduce lose of resolution */ + val = s / dst; + if (n < f_num) { + n = f_num - n; + val <<= n; + val |= ((s % dst) << n) / dst; + } + + return val; +} + +static int mdss_mdp_scale_setup(struct mdss_mdp_pipe *pipe) +{ + u32 scale_config = 0; + u32 phasex_step = 0, phasey_step = 0; + u32 chroma_sample; + + if (pipe->type == MDSS_MDP_PIPE_TYPE_DMA) { + if (!(pipe->flags & MDP_ROT_90) && (pipe->dst.h != pipe->src.h + || pipe->dst.w != pipe->src.w)) + return -EINVAL; /* no scaling supported on dma pipes */ + else + return 0; + } + + chroma_sample = pipe->src_fmt->chroma_sample; + + if ((pipe->src.h != pipe->dst.h) || + (chroma_sample == MDSS_MDP_CHROMA_420) || + (chroma_sample == MDSS_MDP_CHROMA_H1V2)) { + pr_debug("scale y - src_h=%d dst_h=%d\n", + pipe->src.h, pipe->dst.h); + + if ((pipe->src.h / MAX_DOWNSCALE_RATIO) > pipe->dst.h) { + pr_err("too much downscaling height=%d->%d", + pipe->src.h, pipe->dst.h); + return -EINVAL; + } + + scale_config |= MDSS_MDP_SCALEY_EN; + + if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) { + u32 chr_dst_h = pipe->dst.h; + if ((chroma_sample == MDSS_MDP_CHROMA_420) || + (chroma_sample == MDSS_MDP_CHROMA_H1V2)) + chr_dst_h *= 2; /* 2x upsample chroma */ + + if (pipe->src.h <= pipe->dst.h) + scale_config |= /* G/Y, A */ + (MDSS_MDP_SCALE_FILTER_BIL << 10) | + (MDSS_MDP_SCALE_FILTER_NEAREST << 18); + else + scale_config |= /* G/Y, A */ + (MDSS_MDP_SCALE_FILTER_PCMN << 10) | + (MDSS_MDP_SCALE_FILTER_NEAREST << 18); + + if (pipe->src.h <= chr_dst_h) + scale_config |= /* CrCb */ + (MDSS_MDP_SCALE_FILTER_BIL << 14); + else + scale_config |= /* CrCb */ + (MDSS_MDP_SCALE_FILTER_PCMN << 14); + + phasey_step = mdss_mdp_scale_phase_step( + PHASE_STEP_SHIFT, pipe->src.h, chr_dst_h); + + mdss_mdp_pipe_write(pipe, + MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPY, + phasey_step); + } else { + if (pipe->src.h <= pipe->dst.h) + scale_config |= /* RGB, A */ + (MDSS_MDP_SCALE_FILTER_BIL << 10) | + (MDSS_MDP_SCALE_FILTER_NEAREST << 18); + else + scale_config |= /* RGB, A */ + (MDSS_MDP_SCALE_FILTER_PCMN << 10) | + (MDSS_MDP_SCALE_FILTER_NEAREST << 18); + } + + phasey_step = mdss_mdp_scale_phase_step( + PHASE_STEP_SHIFT, pipe->src.h, pipe->dst.h); + } + + if ((pipe->src.w != pipe->dst.w) || + (chroma_sample == MDSS_MDP_CHROMA_420) || + (chroma_sample == MDSS_MDP_CHROMA_H2V1)) { + pr_debug("scale x - src_w=%d dst_w=%d\n", + pipe->src.w, pipe->dst.w); + + if ((pipe->src.w / MAX_DOWNSCALE_RATIO) > pipe->dst.w) { + pr_err("too much downscaling width=%d->%d", + pipe->src.w, pipe->dst.w); + return -EINVAL; + } + + scale_config |= MDSS_MDP_SCALEX_EN; + + if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) { + u32 chr_dst_w = pipe->dst.w; + + if ((chroma_sample == MDSS_MDP_CHROMA_420) || + (chroma_sample == MDSS_MDP_CHROMA_H2V1)) + chr_dst_w *= 2; /* 2x upsample chroma */ + + if (pipe->src.w <= pipe->dst.w) + scale_config |= /* G/Y, A */ + (MDSS_MDP_SCALE_FILTER_BIL << 8) | + (MDSS_MDP_SCALE_FILTER_NEAREST << 16); + else + scale_config |= /* G/Y, A */ + (MDSS_MDP_SCALE_FILTER_PCMN << 8) | + (MDSS_MDP_SCALE_FILTER_NEAREST << 16); + + if (pipe->src.w <= chr_dst_w) + scale_config |= /* CrCb */ + (MDSS_MDP_SCALE_FILTER_BIL << 12); + else + scale_config |= /* CrCb */ + (MDSS_MDP_SCALE_FILTER_PCMN << 12); + + phasex_step = mdss_mdp_scale_phase_step( + PHASE_STEP_SHIFT, pipe->src.w, chr_dst_w); + mdss_mdp_pipe_write(pipe, + MDSS_MDP_REG_VIG_QSEED2_C12_PHASESTEPX, + phasex_step); + } else { + if (pipe->src.w <= pipe->dst.w) + scale_config |= /* RGB, A */ + (MDSS_MDP_SCALE_FILTER_BIL << 8) | + (MDSS_MDP_SCALE_FILTER_NEAREST << 16); + else + scale_config |= /* RGB, A */ + (MDSS_MDP_SCALE_FILTER_PCMN << 8) | + (MDSS_MDP_SCALE_FILTER_NEAREST << 16); + } + + phasex_step = mdss_mdp_scale_phase_step( + PHASE_STEP_SHIFT, pipe->src.w, pipe->dst.w); + } + + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SCALE_CONFIG, scale_config); + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SCALE_PHASE_STEP_X, phasex_step); + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SCALE_PHASE_STEP_Y, phasey_step); + return 0; +} + +static int mdss_mdp_image_setup(struct mdss_mdp_pipe *pipe) +{ + u32 img_size, src_size, src_xy, dst_size, dst_xy, ystride0, ystride1; + + pr_debug("pnum=%d wh=%dx%d src={%d,%d,%d,%d} dst={%d,%d,%d,%d}\n", + pipe->num, pipe->img_width, pipe->img_height, + pipe->src.x, pipe->src.y, pipe->src.w, pipe->src.h, + pipe->dst.x, pipe->dst.y, pipe->dst.w, pipe->dst.h); + + if (mdss_mdp_scale_setup(pipe)) + return -EINVAL; + + mdss_mdp_get_plane_sizes(pipe->src_fmt->format, pipe->img_width, + pipe->img_height, &pipe->src_planes); + + img_size = (pipe->img_height << 16) | pipe->img_width; + src_size = (pipe->src.h << 16) | pipe->src.w; + src_xy = (pipe->src.y << 16) | pipe->src.x; + dst_size = (pipe->dst.h << 16) | pipe->dst.w; + dst_xy = (pipe->dst.y << 16) | pipe->dst.x; + ystride0 = (pipe->src_planes.ystride[0]) | + (pipe->src_planes.ystride[1] << 16); + ystride1 = (pipe->src_planes.ystride[2]) | + (pipe->src_planes.ystride[3] << 16); + + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_IMG_SIZE, img_size); + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_SIZE, src_size); + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_XY, src_xy); + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_OUT_SIZE, dst_size); + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_OUT_XY, dst_xy); + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_YSTRIDE0, ystride0); + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_YSTRIDE1, ystride1); + + return 0; +} + +static int mdss_mdp_format_setup(struct mdss_mdp_pipe *pipe) +{ + struct mdss_mdp_format_params *fmt; + u32 rot90, opmode, chroma_samp; + + fmt = pipe->src_fmt; + + opmode = pipe->bwc_mode; + if (pipe->flags & MDP_FLIP_LR) + opmode |= MDSS_MDP_OP_FLIP_LR; + if (pipe->flags & MDP_FLIP_UD) + opmode |= MDSS_MDP_OP_FLIP_UD; + + pr_debug("pnum=%d format=%d opmode=%x\n", pipe->num, fmt->format, + opmode); + + rot90 = !!(pipe->flags & MDP_SOURCE_ROTATED_90); + + chroma_samp = fmt->chroma_sample; + if (rot90) { + if (chroma_samp == MDSS_MDP_CHROMA_H2V1) + chroma_samp = MDSS_MDP_CHROMA_H1V2; + else if (chroma_samp == MDSS_MDP_CHROMA_H1V2) + chroma_samp = MDSS_MDP_CHROMA_H2V1; + } + + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_FORMAT, + (chroma_samp << 23) | + (fmt->fetch_planes << 19) | + (fmt->unpack_align_msb << 18) | + (fmt->unpack_tight << 17) | + (fmt->unpack_count << 12) | + (rot90 << 11) | + (fmt->bpp << 9) | + (fmt->alpha_enable << 8) | + (fmt->a_bit << 6) | + (fmt->r_bit << 4) | + (fmt->b_bit << 2) | + (fmt->g_bit << 0)); + + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_UNPACK_PATTERN, + (fmt->element3 << 24) | + (fmt->element2 << 16) | + (fmt->element1 << 8) | + (fmt->element0 << 0)); + + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC_OP_MODE, opmode); + + return 0; +} + +static int mdss_mdp_vig_setup(struct mdss_mdp_pipe *pipe) +{ + u32 opmode = 0; + + pr_debug("pnum=%x\n", pipe->num); + + if (pipe->src_fmt->is_yuv) + opmode |= (0 << 19) | /* DST_DATA=RGB */ + (1 << 18) | /* SRC_DATA=YCBCR */ + (1 << 17); /* CSC_1_EN */ + + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_VIG_OP_MODE, opmode); + + return 0; +} + +static int mdss_mdp_src_addr_setup(struct mdss_mdp_pipe *pipe, + struct mdss_mdp_data *data) +{ + int ret; + + pr_debug("pnum=%d\n", pipe->num); + + if (pipe->type != MDSS_MDP_PIPE_TYPE_DMA) + data->bwc_enabled = pipe->bwc_mode; + + ret = mdss_mdp_data_check(data, &pipe->src_planes); + if (ret) + return ret; + + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC0_ADDR, data->p[0].addr); + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC1_ADDR, data->p[1].addr); + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC2_ADDR, data->p[2].addr); + mdss_mdp_pipe_write(pipe, MDSS_MDP_REG_SSPP_SRC3_ADDR, data->p[3].addr); + + return 0; +} + +int mdss_mdp_pipe_queue_data(struct mdss_mdp_pipe *pipe, + struct mdss_mdp_data *src_data) +{ + int ret = 0; + u32 params_changed; + + if (!pipe) { + pr_err("pipe not setup properly for queue\n"); + return -ENODEV; + } + + if (!pipe->mixer) { + pr_err("pipe mixer not setup properly for queue\n"); + return -ENODEV; + } + + pr_debug("pnum=%x mixer=%d\n", pipe->num, pipe->mixer->num); + + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_ON, false); + + params_changed = pipe->params_changed; + if (params_changed) { + pipe->params_changed = 0; + + ret = mdss_mdp_image_setup(pipe); + if (ret) { + pr_err("image setup error for pnum=%d\n", pipe->num); + goto done; + } + + ret = mdss_mdp_format_setup(pipe); + if (ret) { + pr_err("format %d setup error pnum=%d\n", + pipe->src_fmt->format, pipe->num); + goto done; + } + + if (pipe->type == MDSS_MDP_PIPE_TYPE_VIG) + mdss_mdp_vig_setup(pipe); + + ret = mdss_mdp_smp_reserve(pipe); + if (ret) { + pr_err("unable to reserve smp for pnum=%d\n", + pipe->num); + goto done; + } + + mdss_mdp_smp_alloc(pipe); + } + + ret = mdss_mdp_src_addr_setup(pipe, src_data); + if (ret) { + pr_err("addr setup error for pnum=%d\n", pipe->num); + goto done; + } + + mdss_mdp_mixer_pipe_update(pipe, params_changed); + + pipe->play_cnt++; + +done: + mdss_mdp_clk_ctrl(MDP_BLOCK_POWER_OFF, false); + + return ret; +} diff --git a/drivers/video/msm/mdss/mdss_mdp_util.c b/drivers/video/msm/mdss/mdss_mdp_util.c new file mode 100644 index 00000000000..dc35a73b10a --- /dev/null +++ b/drivers/video/msm/mdss/mdss_mdp_util.c @@ -0,0 +1,278 @@ +/* Copyright (c) 2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#define pr_fmt(fmt) "%s: " fmt, __func__ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "mdss_fb.h" +#include "mdss_mdp.h" +#include "mdss_mdp_formats.h" + +enum { + MDP_INTR_VSYNC_INTF_0, + MDP_INTR_VSYNC_INTF_1, + MDP_INTR_VSYNC_INTF_2, + MDP_INTR_VSYNC_INTF_3, + MDP_INTR_PING_PONG_0, + MDP_INTR_PING_PONG_1, + MDP_INTR_PING_PONG_2, + MDP_INTR_WB_0, + MDP_INTR_WB_1, + MDP_INTR_WB_2, + MDP_INTR_MAX, +}; + +struct intr_callback { + void (*func)(void *); + void *arg; +}; + +struct intr_callback mdp_intr_cb[MDP_INTR_MAX]; +static DEFINE_SPINLOCK(mdss_mdp_intr_lock); + +static int mdss_mdp_intr2index(u32 intr_type, u32 intf_num) +{ + int index = -1; + switch (intr_type) { + case MDSS_MDP_IRQ_INTF_VSYNC: + index = MDP_INTR_VSYNC_INTF_0 + intf_num; + break; + case MDSS_MDP_IRQ_PING_PONG_COMP: + index = MDP_INTR_PING_PONG_0 + intf_num; + break; + case MDSS_MDP_IRQ_WB_ROT_COMP: + index = MDP_INTR_WB_0 + intf_num; + break; + case MDSS_MDP_IRQ_WB_WFD: + index = MDP_INTR_WB_2 + intf_num; + break; + } + + return index; +} + +int mdss_mdp_set_intr_callback(u32 intr_type, u32 intf_num, + void (*fnc_ptr)(void *), void *arg) +{ + unsigned long flags; + int index, ret; + + index = mdss_mdp_intr2index(intr_type, intf_num); + if (index < 0) { + pr_warn("invalid intr type=%u intf_num=%u\n", + intr_type, intf_num); + return -EINVAL; + } + + spin_lock_irqsave(&mdss_mdp_intr_lock, flags); + if (!mdp_intr_cb[index].func) { + mdp_intr_cb[index].func = fnc_ptr; + mdp_intr_cb[index].arg = arg; + ret = 0; + } else { + ret = -EBUSY; + } + spin_unlock_irqrestore(&mdss_mdp_intr_lock, flags); + + return ret; +} + +static inline void mdss_mdp_intr_done(int index) +{ + void (*fnc)(void *); + void *arg; + + spin_lock(&mdss_mdp_intr_lock); + fnc = mdp_intr_cb[index].func; + arg = mdp_intr_cb[index].arg; + if (fnc != NULL) + mdp_intr_cb[index].func = NULL; + spin_unlock(&mdss_mdp_intr_lock); + if (fnc) + fnc(arg); +} + +irqreturn_t mdss_mdp_isr(int irq, void *ptr) +{ + u32 isr, mask; + + + isr = MDSS_MDP_REG_READ(MDSS_MDP_REG_INTR_STATUS); + if (isr == 0) + goto done; + + pr_devel("isr=%x\n", isr); + + mask = MDSS_MDP_REG_READ(MDSS_MDP_REG_INTR_EN); + MDSS_MDP_REG_WRITE(MDSS_MDP_REG_INTR_CLEAR, isr); + + isr &= mask; + if (isr == 0) + goto done; + + if (isr & MDSS_MDP_INTR_PING_PONG_0_DONE) + mdss_mdp_intr_done(MDP_INTR_PING_PONG_0); + + if (isr & MDSS_MDP_INTR_PING_PONG_1_DONE) + mdss_mdp_intr_done(MDP_INTR_PING_PONG_1); + + if (isr & MDSS_MDP_INTR_PING_PONG_2_DONE) + mdss_mdp_intr_done(MDP_INTR_PING_PONG_2); + + if (isr & MDSS_MDP_INTR_INTF_0_VSYNC) + mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_0); + + if (isr & MDSS_MDP_INTR_INTF_1_VSYNC) + mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_1); + + if (isr & MDSS_MDP_INTR_INTF_2_VSYNC) + mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_2); + + if (isr & MDSS_MDP_INTR_INTF_3_VSYNC) + mdss_mdp_intr_done(MDP_INTR_VSYNC_INTF_3); + + if (isr & MDSS_MDP_INTR_WB_0_DONE) + mdss_mdp_intr_done(MDP_INTR_WB_0); + + if (isr & MDSS_MDP_INTR_WB_1_DONE) + mdss_mdp_intr_done(MDP_INTR_WB_1); + + if (isr & MDSS_MDP_INTR_WB_2_DONE) + mdss_mdp_intr_done(MDP_INTR_WB_2); + +done: + return IRQ_HANDLED; +} + +struct mdss_mdp_format_params *mdss_mdp_get_format_params(u32 format) +{ + struct mdss_mdp_format_params *fmt = NULL; + if (format < MDP_IMGTYPE_LIMIT) { + fmt = &mdss_mdp_format_map[format]; + if (fmt->format != format) + fmt = NULL; + } + + return fmt; +} + +int mdss_mdp_get_plane_sizes(u32 format, u32 w, u32 h, + struct mdss_mdp_plane_sizes *ps) +{ + struct mdss_mdp_format_params *fmt; + int i; + + if (ps == NULL) + return -EINVAL; + + if ((w > MAX_IMG_WIDTH) || (h > MAX_IMG_HEIGHT)) + return -ERANGE; + + fmt = mdss_mdp_get_format_params(format); + if (!fmt) + return -EINVAL; + + memset(ps, 0, sizeof(struct mdss_mdp_plane_sizes)); + + if (fmt->fetch_planes == MDSS_MDP_PLANE_INTERLEAVED) { + u32 bpp = fmt->bpp + 1; + ps->num_planes = 1; + ps->plane_size[0] = w * h * bpp; + ps->ystride[0] = w * bpp; + } else { + u8 hmap[] = { 1, 2, 1, 2 }; + u8 vmap[] = { 1, 1, 2, 2 }; + u8 horiz, vert; + + horiz = hmap[fmt->chroma_sample]; + vert = vmap[fmt->chroma_sample]; + + if (format == MDP_Y_CR_CB_GH2V2) { + ps->plane_size[0] = ALIGN(w, 16) * h; + ps->plane_size[1] = ALIGN(w / horiz, 16) * (h / vert); + ps->ystride[0] = ALIGN(w, 16); + ps->ystride[1] = ALIGN(w / horiz, 16); + } else { + ps->plane_size[0] = w * h; + ps->plane_size[1] = (w / horiz) * (h / vert); + ps->ystride[0] = w; + ps->ystride[1] = (w / horiz); + } + + if (fmt->fetch_planes == MDSS_MDP_PLANE_PSEUDO_PLANAR) { + ps->num_planes = 2; + ps->plane_size[1] *= 2; + ps->ystride[1] *= 2; + } else { /* planar */ + ps->num_planes = 3; + ps->plane_size[2] = ps->plane_size[1]; + ps->ystride[2] = ps->ystride[1]; + } + } + + for (i = 0; i < ps->num_planes; i++) + ps->total_size += ps->plane_size[i]; + + return 0; +} + +int mdss_mdp_data_check(struct mdss_mdp_data *data, + struct mdss_mdp_plane_sizes *ps) +{ + if (!ps) + return 0; + + if (!data || data->num_planes == 0) + return -ENOMEM; + + if (data->bwc_enabled) { + return -EPERM; /* not supported */ + } else { + struct mdss_mdp_img_data *prev, *curr; + int i; + + pr_debug("srcp0=%x len=%u frame_size=%u\n", data->p[0].addr, + data->p[0].len, ps->total_size); + + for (i = 0; i < ps->num_planes; i++) { + curr = &data->p[i]; + if (i >= data->num_planes) { + u32 psize = ps->plane_size[i-1]; + prev = &data->p[i-1]; + if (prev->len > psize) { + curr->len = prev->len - psize; + prev->len = psize; + } + curr->addr = prev->addr + psize; + } + if (curr->len < ps->plane_size[i]) { + pr_err("insufficient mem=%u p=%d len=%u\n", + curr->len, i, ps->plane_size[i]); + return -ENOMEM; + } + pr_debug("plane[%d] addr=%x len=%u\n", i, + curr->addr, curr->len); + } + data->num_planes = ps->num_planes; + } + + return 0; +} diff --git a/drivers/video/msm/mdss/mdss_panel.h b/drivers/video/msm/mdss/mdss_panel.h new file mode 100644 index 00000000000..3fd943d59d8 --- /dev/null +++ b/drivers/video/msm/mdss/mdss_panel.h @@ -0,0 +1,176 @@ +/* Copyright (c) 2008-2012, Code Aurora Forum. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * 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. + * + */ + +#ifndef MDSS_PANEL_H +#define MDSS_PANEL_H + +#include +#include + +/* panel id type */ +struct panel_id { + u16 id; + u16 type; +}; + +/* panel type list */ +#define NO_PANEL 0xffff /* No Panel */ +#define MDDI_PANEL 1 /* MDDI */ +#define EBI2_PANEL 2 /* EBI2 */ +#define LCDC_PANEL 3 /* internal LCDC type */ +#define EXT_MDDI_PANEL 4 /* Ext.MDDI */ +#define TV_PANEL 5 /* TV */ +#define HDMI_PANEL 6 /* HDMI TV */ +#define DTV_PANEL 7 /* DTV */ +#define MIPI_VIDEO_PANEL 8 /* MIPI */ +#define MIPI_CMD_PANEL 9 /* MIPI */ +#define WRITEBACK_PANEL 10 /* Wifi display */ +#define LVDS_PANEL 11 /* LVDS */ +#define EDP_PANEL 12 /* LVDS */ + +/* panel class */ +enum { + DISPLAY_LCD = 0, /* lcd = ebi2/mddi */ + DISPLAY_LCDC, /* lcdc */ + DISPLAY_TV, /* TV Out */ + DISPLAY_EXT_MDDI, /* External MDDI */ + DISPLAY_WRITEBACK, +}; + +/* panel device locaiton */ +enum { + DISPLAY_1 = 0, /* attached as first device */ + DISPLAY_2, /* attached on second device */ + DISPLAY_3, /* attached on third writeback device */ + MAX_PHYS_TARGET_NUM, +}; + +/* panel info type */ +struct lcd_panel_info { + u32 vsync_enable; + u32 refx100; + u32 v_back_porch; + u32 v_front_porch; + u32 v_pulse_width; + u32 hw_vsync_mode; + u32 vsync_notifier_period; + u32 rev; +}; + +struct lcdc_panel_info { + u32 h_back_porch; + u32 h_front_porch; + u32 h_pulse_width; + u32 v_back_porch; + u32 v_front_porch; + u32 v_pulse_width; + u32 border_clr; + u32 underflow_clr; + u32 hsync_skew; + /* Pad width */ + u32 xres_pad; + /* Pad height */ + u32 yres_pad; +}; + +struct mipi_panel_info { + char mode; /* video/cmd */ + char interleave_mode; + char crc_check; + char ecc_check; + char dst_format; /* shared by video and command */ + char data_lane0; + char data_lane1; + char data_lane2; + char data_lane3; + char dlane_swap; /* data lane swap */ + char rgb_swap; + char b_sel; + char g_sel; + char r_sel; + char rx_eot_ignore; + char tx_eot_append; + char t_clk_post; /* 0xc0, DSI_CLKOUT_TIMING_CTRL */ + char t_clk_pre; /* 0xc0, DSI_CLKOUT_TIMING_CTRL */ + char vc; /* virtual channel */ + struct mipi_dsi_phy_ctrl *dsi_phy_db; + /* video mode */ + char pulse_mode_hsa_he; + char hfp_power_stop; + char hbp_power_stop; + char hsa_power_stop; + char eof_bllp_power_stop; + char bllp_power_stop; + char traffic_mode; + char frame_rate; + /* command mode */ + char interleave_max; + char insert_dcs_cmd; + char wr_mem_continue; + char wr_mem_start; + char te_sel; + char stream; /* 0 or 1 */ + char mdp_trigger; + char dma_trigger; + u32 dsi_pclk_rate; + /* The packet-size should not bet changed */ + char no_max_pkt_size; + /* Clock required during LP commands */ + char force_clk_lane_hs; +}; + +enum lvds_mode { + LVDS_SINGLE_CHANNEL_MODE, + LVDS_DUAL_CHANNEL_MODE, +}; + +struct lvds_panel_info { + enum lvds_mode channel_mode; + /* Channel swap in dual mode */ + char channel_swap; +}; + +struct mdss_panel_info { + u32 xres; + u32 yres; + u32 bpp; + u32 type; + u32 wait_cycle; + u32 pdest; + u32 bl_max; + u32 bl_min; + u32 fb_num; + u32 clk_rate; + u32 clk_min; + u32 clk_max; + u32 frame_count; + u32 is_3d_panel; + u32 out_format; + + struct lcd_panel_info lcd; + struct lcdc_panel_info lcdc; + struct mipi_panel_info mipi; + struct lvds_panel_info lvds; +}; + +struct mdss_panel_data { + struct mdss_panel_info panel_info; + void (*set_backlight) (u32 bl_level); + + /* function entry chain */ + int (*on) (struct mdss_panel_data *pdata); + int (*off) (struct mdss_panel_data *pdata); +}; + +int mdss_register_panel(struct mdss_panel_data *pdata); +#endif /* MDSS_PANEL_H */ diff --git a/include/linux/msm_mdp.h b/include/linux/msm_mdp.h index f757fae3f77..8b6351fe3ec 100644 --- a/include/linux/msm_mdp.h +++ b/include/linux/msm_mdp.h @@ -86,6 +86,8 @@ enum { MDP_YCRYCB_H2V1, /* YCrYCb interleave */ MDP_Y_CRCB_H2V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ MDP_Y_CBCR_H2V1, /* Y and CrCb, pseduo planer w/ Cr is in MSB */ + MDP_Y_CRCB_H1V2, + MDP_Y_CBCR_H1V2, MDP_RGBA_8888, /* ARGB 888 */ MDP_BGRA_8888, /* ABGR 888 */ MDP_RGBX_8888, /* RGBX 888 */ @@ -98,10 +100,10 @@ enum { MDP_Y_CBCR_H1V1, /* Y and CbCr, pseduo planer w/ Cb is in MSB */ MDP_YCRCB_H1V1, /* YCrCb interleave */ MDP_YCBCR_H1V1, /* YCbCr interleave */ + MDP_BGR_565, /* BGR 565 planer */ MDP_IMGTYPE_LIMIT, MDP_RGB_BORDERFILL, /* border fill pipe */ - MDP_BGR_565 = MDP_IMGTYPE2_START, /* BGR 565 planer */ - MDP_FB_FORMAT, /* framebuffer format */ + MDP_FB_FORMAT = MDP_IMGTYPE2_START, /* framebuffer format */ MDP_IMGTYPE_LIMIT2 /* Non valid image type after this enum */ }; @@ -118,6 +120,8 @@ enum { NUM_HSIC_PARAM, }; +#define MDSS_MDP_RIGHT_MIXER 0x100 + /* mdp_blit_req flag values */ #define MDP_ROT_NOP 0 #define MDP_FLIP_LR 0x1