diff options
Diffstat (limited to 'drivers/video/fbdev')
39 files changed, 1194 insertions, 78 deletions
diff --git a/drivers/video/fbdev/Kconfig b/drivers/video/fbdev/Kconfig index 759aaeb6a196..a6c67e189efa 100644 --- a/drivers/video/fbdev/Kconfig +++ b/drivers/video/fbdev/Kconfig @@ -1397,6 +1397,7 @@ config FB_ATY select FB_CFB_IMAGEBLIT select FB_BACKLIGHT if FB_ATY_BACKLIGHT select FB_MACMODES if PPC + select FB_ATY_CT if SPARC64 && PCI help This driver supports graphics boards with the ATI Mach64 chips. Say Y if you have such a graphics board. @@ -1407,7 +1408,6 @@ config FB_ATY config FB_ATY_CT bool "Mach64 CT/VT/GT/LT (incl. 3D RAGE) support" depends on PCI && FB_ATY - default y if SPARC64 && PCI help Say Y here to support use of ATI's 64-bit Rage boards (or other boards based on the Mach64 CT, VT, GT, and LT chipsets) as a diff --git a/drivers/video/fbdev/asiliantfb.c b/drivers/video/fbdev/asiliantfb.c index 7e8ddf00ccc2..dbcc6ebaf904 100644 --- a/drivers/video/fbdev/asiliantfb.c +++ b/drivers/video/fbdev/asiliantfb.c @@ -227,6 +227,9 @@ static int asiliantfb_check_var(struct fb_var_screeninfo *var, { unsigned long Ftarget, ratio, remainder; + if (!var->pixclock) + return -EINVAL; + ratio = 1000000 / var->pixclock; remainder = 1000000 % var->pixclock; Ftarget = 1000000 * ratio + (1000000 * remainder) / var->pixclock; diff --git a/drivers/video/fbdev/chipsfb.c b/drivers/video/fbdev/chipsfb.c index 59abdc6a97f6..84a3778552eb 100644 --- a/drivers/video/fbdev/chipsfb.c +++ b/drivers/video/fbdev/chipsfb.c @@ -332,7 +332,7 @@ static struct fb_var_screeninfo chipsfb_var = { static void init_chips(struct fb_info *p, unsigned long addr) { - memset(p->screen_base, 0, 0x100000); + fb_memset(p->screen_base, 0, 0x100000); p->fix = chipsfb_fix; p->fix.smem_start = addr; @@ -350,7 +350,7 @@ static void init_chips(struct fb_info *p, unsigned long addr) static int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) { struct fb_info *p; - unsigned long addr, size; + unsigned long addr; unsigned short cmd; int rc = -ENODEV; @@ -362,7 +362,6 @@ static int chipsfb_pci_init(struct pci_dev *dp, const struct pci_device_id *ent) if ((dp->resource[0].flags & IORESOURCE_MEM) == 0) goto err_disable; addr = pci_resource_start(dp, 0); - size = pci_resource_len(dp, 0); if (addr == 0) goto err_disable; diff --git a/drivers/video/fbdev/core/fbcmap.c b/drivers/video/fbdev/core/fbcmap.c index 1f8b480b1a4d..1555106ae38f 100644 --- a/drivers/video/fbdev/core/fbcmap.c +++ b/drivers/video/fbdev/core/fbcmap.c @@ -101,17 +101,17 @@ int fb_alloc_cmap_gfp(struct fb_cmap *cmap, int len, int transp, gfp_t flags) if (!len) return 0; - cmap->red = kmalloc(size, flags); + cmap->red = kzalloc(size, flags); if (!cmap->red) goto fail; - cmap->green = kmalloc(size, flags); + cmap->green = kzalloc(size, flags); if (!cmap->green) goto fail; - cmap->blue = kmalloc(size, flags); + cmap->blue = kzalloc(size, flags); if (!cmap->blue) goto fail; if (transp) { - cmap->transp = kmalloc(size, flags); + cmap->transp = kzalloc(size, flags); if (!cmap->transp) goto fail; } else { diff --git a/drivers/video/fbdev/core/fbmem.c b/drivers/video/fbdev/core/fbmem.c index 4bb5d68189a0..cf533f1fa699 100644 --- a/drivers/video/fbdev/core/fbmem.c +++ b/drivers/video/fbdev/core/fbmem.c @@ -32,6 +32,7 @@ #include <linux/device.h> #include <linux/efi.h> #include <linux/fb.h> +#include <linux/overflow.h> #include <asm/fb.h> @@ -981,6 +982,7 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) if ((var->activate & FB_ACTIVATE_FORCE) || memcmp(&info->var, var, sizeof(struct fb_var_screeninfo))) { u32 activate = var->activate; + u32 unused; /* When using FOURCC mode, make sure the red, green, blue and * transp fields are set to 0. @@ -1001,6 +1003,15 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var) goto done; } + /* bitfill_aligned() assumes that it's at least 8x8 */ + if (var->xres < 8 || var->yres < 8) + return -EINVAL; + + /* Too huge resolution causes multiplication overflow. */ + if (check_mul_overflow(var->xres, var->yres, &unused) || + check_mul_overflow(var->xres_virtual, var->yres_virtual, &unused)) + return -EINVAL; + ret = info->fbops->fb_check_var(var, info); if (ret) @@ -1139,7 +1150,7 @@ static long do_fb_ioctl(struct fb_info *info, unsigned int cmd, case FBIOGET_FSCREENINFO: if (!lock_fb_info(info)) return -ENODEV; - fix = info->fix; + memcpy(&fix, &info->fix, sizeof(fix)); unlock_fb_info(info); ret = copy_to_user(argp, &fix, sizeof(fix)) ? -EFAULT : 0; diff --git a/drivers/video/fbdev/geode/video_gx.c b/drivers/video/fbdev/geode/video_gx.c index 6082f653c68a..67773e8bbb95 100644 --- a/drivers/video/fbdev/geode/video_gx.c +++ b/drivers/video/fbdev/geode/video_gx.c @@ -127,7 +127,7 @@ void gx_set_dclk_frequency(struct fb_info *info) int timeout = 1000; /* Rev. 1 Geode GXs use a 14 MHz reference clock instead of 48 MHz. */ - if (cpu_data(0).x86_mask == 1) { + if (cpu_data(0).x86_stepping == 1) { pll_table = gx_pll_table_14MHz; pll_table_len = ARRAY_SIZE(gx_pll_table_14MHz); } else { diff --git a/drivers/video/fbdev/hgafb.c b/drivers/video/fbdev/hgafb.c index 4a397c7c1b56..46ac8bbb376d 100644 --- a/drivers/video/fbdev/hgafb.c +++ b/drivers/video/fbdev/hgafb.c @@ -286,7 +286,7 @@ static int hga_card_detect(void) hga_vram = ioremap(0xb0000, hga_vram_len); if (!hga_vram) - goto error; + return -ENOMEM; if (request_region(0x3b0, 12, "hgafb")) release_io_ports = 1; @@ -346,13 +346,18 @@ static int hga_card_detect(void) hga_type_name = "Hercules"; break; } - return 1; + return 0; error: if (release_io_ports) release_region(0x3b0, 12); if (release_io_port) release_region(0x3bf, 1); - return 0; + + iounmap(hga_vram); + + pr_err("hgafb: HGA card not detected.\n"); + + return -EINVAL; } /** @@ -550,13 +555,11 @@ static struct fb_ops hgafb_ops = { static int hgafb_probe(struct platform_device *pdev) { struct fb_info *info; + int ret; - if (! hga_card_detect()) { - printk(KERN_INFO "hgafb: HGA card not detected.\n"); - if (hga_vram) - iounmap(hga_vram); - return -EINVAL; - } + ret = hga_card_detect(); + if (ret) + return ret; printk(KERN_INFO "hgafb: %s with %ldK of memory detected.\n", hga_type_name, hga_vram_len/1024); diff --git a/drivers/video/fbdev/hyperv_fb.c b/drivers/video/fbdev/hyperv_fb.c index e2451bdb4525..883c06381e7c 100644 --- a/drivers/video/fbdev/hyperv_fb.c +++ b/drivers/video/fbdev/hyperv_fb.c @@ -712,7 +712,10 @@ static int hvfb_getmem(struct hv_device *hdev, struct fb_info *info) goto err1; } - fb_virt = ioremap(par->mem->start, screen_fb_size); + /* + * Map the VRAM cacheable for performance. + */ + fb_virt = ioremap_wc(par->mem->start, screen_fb_size); if (!fb_virt) goto err2; diff --git a/drivers/video/fbdev/imsttfb.c b/drivers/video/fbdev/imsttfb.c index 4994a540f680..9b167f7ef6c6 100644 --- a/drivers/video/fbdev/imsttfb.c +++ b/drivers/video/fbdev/imsttfb.c @@ -1517,11 +1517,6 @@ static int imsttfb_probe(struct pci_dev *pdev, const struct pci_device_id *ent) info->fix.smem_start = addr; info->screen_base = (__u8 *)ioremap(addr, par->ramdac == IBM ? 0x400000 : 0x800000); - if (!info->screen_base) { - release_mem_region(addr, size); - framebuffer_release(info); - return -ENOMEM; - } info->fix.mmio_start = addr + 0x800000; par->dc_regs = ioremap(addr + 0x800000, 0x1000); par->cmap_regs_phys = addr + 0x840000; diff --git a/drivers/video/fbdev/kyro/fbdev.c b/drivers/video/fbdev/kyro/fbdev.c index 5bb01533271e..d98c3f5d80df 100644 --- a/drivers/video/fbdev/kyro/fbdev.c +++ b/drivers/video/fbdev/kyro/fbdev.c @@ -372,6 +372,11 @@ static int kyro_dev_overlay_viewport_set(u32 x, u32 y, u32 ulWidth, u32 ulHeight /* probably haven't called CreateOverlay yet */ return -EINVAL; + if (ulWidth == 0 || ulWidth == 0xffffffff || + ulHeight == 0 || ulHeight == 0xffffffff || + (x < 2 && ulWidth + 2 == 0)) + return -EINVAL; + /* Stop Ramdac Output */ DisableRamdacOutput(deviceInfo.pSTGReg); @@ -394,6 +399,9 @@ static int kyrofb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) { struct kyrofb_info *par = info->par; + if (!var->pixclock) + return -EINVAL; + if (var->bits_per_pixel != 16 && var->bits_per_pixel != 32) { printk(KERN_WARNING "kyrofb: depth not supported: %u\n", var->bits_per_pixel); return -EINVAL; diff --git a/drivers/video/fbdev/msm/Makefile b/drivers/video/fbdev/msm/Makefile index e101b873f361..dfbc604dafa5 100644 --- a/drivers/video/fbdev/msm/Makefile +++ b/drivers/video/fbdev/msm/Makefile @@ -64,3 +64,5 @@ obj-$(CONFIG_FB_MSM_QPIC_ILI_QVGA_PANEL) += qpic_panel_ili_qvga.o obj-$(CONFIG_FB_MSM_MDSS) += mdss_fb.o mdss_util.o obj-$(CONFIG_COMPAT) += mdss_compat_utils.o + +obj-$(CONFIG_FB_MSM_MDSS) += mdss_livedisplay.o diff --git a/drivers/video/fbdev/msm/mdss_dsi.c b/drivers/video/fbdev/msm/mdss_dsi.c index bffbb731dd4a..a201e272acbc 100644 --- a/drivers/video/fbdev/msm/mdss_dsi.c +++ b/drivers/video/fbdev/msm/mdss_dsi.c @@ -34,6 +34,7 @@ #include "mdss_debug.h" #include "mdss_dsi_phy.h" #include "mdss_dba_utils.h" +#include "mdss_livedisplay.h" #define CMDLINE_DSI_CTL_NUM_STRING_LEN 2 @@ -3112,6 +3113,9 @@ static int mdss_dsi_event_handler(struct mdss_panel_data *pdata, rc); } break; + case MDSS_EVENT_UPDATE_LIVEDISPLAY: + rc = mdss_livedisplay_update(ctrl_pdata, (int)(unsigned long) arg); + break; default: pr_debug("%s: unhandled event=%d\n", __func__, event); break; diff --git a/drivers/video/fbdev/msm/mdss_dsi_panel.c b/drivers/video/fbdev/msm/mdss_dsi_panel.c index fd269d7baf09..1ab256cd08af 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_panel.c +++ b/drivers/video/fbdev/msm/mdss_dsi_panel.c @@ -26,6 +26,7 @@ #include "mdss_dsi.h" #include "mdss_dba_utils.h" #include "mdss_debug.h" +#include "mdss_livedisplay.h" #define DT_CMD_HDR 6 #define DEFAULT_MDP_TRANSFER_TIME 14000 @@ -181,7 +182,7 @@ static void mdss_dsi_panel_apply_settings(struct mdss_dsi_ctrl_pdata *ctrl, } -static void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, +void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, struct dsi_panel_cmds *pcmds, u32 flags) { struct dcs_cmd_req cmdreq; @@ -959,6 +960,10 @@ static int mdss_dsi_panel_on(struct mdss_panel_data *pdata) /* Ensure low persistence mode is set as before */ mdss_dsi_panel_apply_display_setting(pdata, pinfo->persist_mode); + if (pdata->event_handler) + pdata->event_handler(pdata, MDSS_EVENT_UPDATE_LIVEDISPLAY, + (void *)(unsigned long) MODE_UPDATE_ALL); + end: pr_debug("%s:-\n", __func__); return ret; @@ -2979,6 +2984,8 @@ static int mdss_panel_parse_dt(struct device_node *np, pinfo->esc_clk_rate_hz = MDSS_DSI_MAX_ESC_CLK_RATE_HZ; pr_debug("%s: esc clk %d\n", __func__, pinfo->esc_clk_rate_hz); + mdss_livedisplay_parse_dt(np, pinfo); + return 0; error: diff --git a/drivers/video/fbdev/msm/mdss_dsi_status.c b/drivers/video/fbdev/msm/mdss_dsi_status.c index 0f24f66dbcc6..64e9739a46f5 100644 --- a/drivers/video/fbdev/msm/mdss_dsi_status.c +++ b/drivers/video/fbdev/msm/mdss_dsi_status.c @@ -213,7 +213,8 @@ static int fb_event_callback(struct notifier_block *self, return 0; } -static int param_dsi_status_disable(const char *val, struct kernel_param *kp) +static int param_dsi_status_disable(const char *val, + const struct kernel_param *kp) { int ret = 0; int int_val; @@ -228,7 +229,7 @@ static int param_dsi_status_disable(const char *val, struct kernel_param *kp) return ret; } -static int param_set_interval(const char *val, struct kernel_param *kp) +static int param_set_interval(const char *val, const struct kernel_param *kp) { int ret = 0; int int_val; diff --git a/drivers/video/fbdev/msm/mdss_fb.c b/drivers/video/fbdev/msm/mdss_fb.c index a7f21c3a4f18..64f86084b01d 100644 --- a/drivers/video/fbdev/msm/mdss_fb.c +++ b/drivers/video/fbdev/msm/mdss_fb.c @@ -56,6 +56,8 @@ #include "mdss_smmu.h" #include "mdss_mdp.h" +#include "mdss_livedisplay.h" + #ifdef CONFIG_FB_MSM_TRIPLE_BUFFER #define MDSS_FB_NUM 3 #else @@ -518,12 +520,17 @@ static void __mdss_fb_idle_notify_work(struct work_struct *work) /* Notify idle-ness here */ pr_debug("Idle timeout %dms expired!\n", mfd->idle_time); - if (mfd->idle_time) - sysfs_notify(&mfd->fbi->dev->kobj, NULL, "idle_notify"); + mfd->idle_state = MDSS_FB_IDLE; + /* + * idle_notify node events are used to reduce MDP load when idle, + * this is not needed for command mode panels. + */ + if (mfd->idle_time && mfd->panel.type != MIPI_CMD_PANEL) + sysfs_notify(&mfd->fbi->dev->kobj, NULL, "idle_notify"); + sysfs_notify(&mfd->fbi->dev->kobj, NULL, "idle_state"); } - static ssize_t mdss_fb_get_fps_info(struct device *dev, struct device_attribute *attr, char *buf) { @@ -584,6 +591,26 @@ static ssize_t mdss_fb_get_idle_notify(struct device *dev, return ret; } +static ssize_t mdss_fb_get_idle_state(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = fbi->par; + const char *state_strs[] = { + [MDSS_FB_NOT_IDLE] = "active", + [MDSS_FB_IDLE_TIMER_RUNNING] = "pending", + [MDSS_FB_IDLE] = "idle", + }; + int state = mfd->idle_state; + const char *s; + if (state < ARRAY_SIZE(state_strs) && state_strs[state]) + s = state_strs[state]; + else + s = "invalid"; + + return scnprintf(buf, PAGE_SIZE, "%s\n", s); +} + static ssize_t mdss_fb_get_panel_info(struct device *dev, struct device_attribute *attr, char *buf) { @@ -920,6 +947,7 @@ static DEVICE_ATTR(show_blank_event, S_IRUGO, mdss_mdp_show_blank_event, NULL); static DEVICE_ATTR(idle_time, S_IRUGO | S_IWUSR | S_IWGRP, mdss_fb_get_idle_time, mdss_fb_set_idle_time); static DEVICE_ATTR(idle_notify, S_IRUGO, mdss_fb_get_idle_notify, NULL); +static DEVICE_ATTR(idle_state, S_IRUGO, mdss_fb_get_idle_state, NULL); static DEVICE_ATTR(msm_fb_panel_info, S_IRUGO, mdss_fb_get_panel_info, NULL); static DEVICE_ATTR(msm_fb_src_split_info, S_IRUGO, mdss_fb_get_src_split_info, NULL); @@ -941,6 +969,7 @@ static struct attribute *mdss_fb_attrs[] = { &dev_attr_show_blank_event.attr, &dev_attr_idle_time.attr, &dev_attr_idle_notify.attr, + &dev_attr_idle_state.attr, &dev_attr_msm_fb_panel_info.attr, &dev_attr_msm_fb_src_split_info.attr, &dev_attr_msm_fb_thermal_level.attr, @@ -963,7 +992,7 @@ static int mdss_fb_create_sysfs(struct msm_fb_data_type *mfd) 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; + return mdss_livedisplay_create_sysfs(mfd); } static void mdss_fb_remove_sysfs(struct msm_fb_data_type *mfd) @@ -1278,6 +1307,11 @@ static int mdss_fb_probe(struct platform_device *pdev) mfd->index = fbi_list_index; mfd->mdp_fb_page_protection = MDP_FB_PAGE_PROTECTION_WRITECOMBINE; + if (!lcd_backlight_registered) { + backlight_led.brightness = mfd->panel_info->brightness_max; + backlight_led.max_brightness = mfd->panel_info->brightness_max; + } + mfd->ext_ad_ctrl = -1; if (mfd->panel_info && mfd->panel_info->brightness_max > 0) MDSS_BRIGHT_TO_BL(mfd->bl_level, backlight_led.brightness, @@ -1344,8 +1378,6 @@ static int mdss_fb_probe(struct platform_device *pdev) /* android supports only one lcd-backlight/lcd for now */ if (!lcd_backlight_registered) { - backlight_led.brightness = mfd->panel_info->brightness_max; - backlight_led.max_brightness = mfd->panel_info->brightness_max; if (led_classdev_register(&pdev->dev, &backlight_led)) pr_err("led_classdev_register failed\n"); else @@ -3131,14 +3163,18 @@ static int __mdss_fb_sync_buf_done_callback(struct notifier_block *p, ret = __mdss_fb_wait_for_fence_sub(sync_pt_data, sync_pt_data->temp_fen, fence_cnt); } - if (mfd->idle_time && !mod_delayed_work(system_wq, + if (mfd->idle_time) { + if (!mod_delayed_work(system_wq, &mfd->idle_notify_work, msecs_to_jiffies(mfd->idle_time))) - pr_debug("fb%d: restarted idle work\n", - mfd->index); + pr_debug("fb%d: restarted idle work\n", + mfd->index); + mfd->idle_state = MDSS_FB_IDLE_TIMER_RUNNING; + } else { + mfd->idle_state = MDSS_FB_IDLE; + } if (ret == -ETIME) ret = NOTIFY_BAD; - mfd->idle_state = MDSS_FB_IDLE_TIMER_RUNNING; break; case MDP_NOTIFY_FRAME_FLUSHED: pr_debug("%s: frame flushed\n", sync_pt_data->fence_name); diff --git a/drivers/video/fbdev/msm/mdss_hdmi_cec.c b/drivers/video/fbdev/msm/mdss_hdmi_cec.c index 12a9267f3749..5fe3f710c29e 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_cec.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_cec.c @@ -1,4 +1,4 @@ -/* Copyright (c) 2010-2017, 2020 The Linux Foundation. All rights reserved. +/* Copyright (c) 2010-2017,2020-2021, The Linux Foundation. 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 @@ -469,8 +469,12 @@ static int hdmi_cec_enable(void *input, bool enable) } if (enable) { - /* 19.2Mhz * 0.00005 us = 950 = 0x3B6 */ - DSS_REG_W(io, HDMI_CEC_REFTIMER, (0x3B6 & 0xFFF) | BIT(16)); + /* + * 19.2Mhz * 0.00005 us = 960 = 0x3C0 + * CEC Rd/Wr logic is properly working with + * finetuned value of 0x3D4 = 51 us. + */ + DSS_REG_W(io, HDMI_CEC_REFTIMER, (0x3D4 & 0xFFF) | BIT(16)); hdmi_hw_version = DSS_REG_R(io, HDMI_VERSION); if (hdmi_hw_version >= CEC_SUPPORTED_HW_VERSION) { diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.c b/drivers/video/fbdev/msm/mdss_hdmi_edid.c index 6f0234500d70..5095f4ae49b5 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.c @@ -65,6 +65,11 @@ #define EDID_VENDOR_ID_SIZE 4 #define EDID_IEEE_REG_ID 0x0c03 +enum edid_screen_orientation { + LANDSCAPE = 1, + PORTRAIT = 2, +}; + enum edid_sink_mode { SINK_MODE_DVI, SINK_MODE_HDMI @@ -143,6 +148,8 @@ struct hdmi_edid_ctrl { u8 cea_blks; /* DC: MSB -> LSB: Y420_48|Y420_36|Y420_30|RGB48|RGB36|RGB30|Y444 */ u8 deep_color; + u8 physical_width; + u8 physical_height; u16 physical_address; u32 video_resolution; /* selected by user */ u32 sink_mode; /* HDMI or DVI */ @@ -167,6 +174,9 @@ struct hdmi_edid_ctrl { bool y420_cmdb_supports_all; struct hdmi_edid_y420_cmdb y420_cmdb; + enum edid_screen_orientation orientation; + enum aspect_ratio aspect_ratio; + struct hdmi_edid_sink_data sink_data; struct hdmi_edid_init_data init_data; struct hdmi_edid_sink_caps sink_caps; @@ -254,6 +264,11 @@ int hdmi_edid_reset_parser(void *input) edid_ctrl->y420_cmdb_supports_all = false; kfree(edid_ctrl->y420_cmdb.vic_list); memset(&edid_ctrl->y420_cmdb, 0, sizeof(edid_ctrl->y420_cmdb)); + + edid_ctrl->physical_width = 0; + edid_ctrl->physical_height = 0; + edid_ctrl->orientation = 0; + edid_ctrl->aspect_ratio = HDMI_RES_AR_INVALID; return 0; } @@ -449,6 +464,30 @@ static ssize_t hdmi_edid_sysfs_rda_modes(struct device *dev, static DEVICE_ATTR(edid_modes, S_IRUGO | S_IWUSR, hdmi_edid_sysfs_rda_modes, hdmi_edid_sysfs_wta_modes); +static ssize_t hdmi_edid_sysfs_rda_screen_size(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t ret = 0; + struct hdmi_edid_ctrl *edid_ctrl = hdmi_edid_get_ctrl(dev); + + if (!edid_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); + return -EINVAL; + } + + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "%d", + (edid_ctrl->physical_width * 10)); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, " %d", + (edid_ctrl->physical_height * 10)); + + DEV_DBG("%s: '%s'\n", __func__, buf); + ret += scnprintf(buf + ret, PAGE_SIZE - ret, "\n"); + + return ret; +} /* hdmi_edid_sysfs_rda_screen_size */ +static DEVICE_ATTR(edid_screen_size, S_IRUGO, hdmi_edid_sysfs_rda_screen_size, + NULL); + static ssize_t hdmi_edid_sysfs_rda_res_info_data(struct device *dev, struct device_attribute *attr, char *buf) { @@ -855,6 +894,7 @@ static DEVICE_ATTR(hdr_data, S_IRUGO, hdmi_edid_sysfs_rda_hdr_data, NULL); static struct attribute *hdmi_edid_fs_attrs[] = { &dev_attr_edid_modes.attr, + &dev_attr_edid_screen_size.attr, &dev_attr_pa.attr, &dev_attr_scan_info.attr, &dev_attr_edid_3d_modes.attr, @@ -1515,6 +1555,68 @@ static u32 hdmi_edid_extract_ieee_reg_id(struct hdmi_edid_ctrl *edid_ctrl, return ((u32)vsd[3] << 16) + ((u32)vsd[2] << 8) + (u32)vsd[1]; } /* hdmi_edid_extract_ieee_reg_id */ +static void hdmi_edid_extract_bdpf(struct hdmi_edid_ctrl *edid_ctrl) +{ + u8 *edid_buf = NULL; + + if (!edid_ctrl) { + DEV_ERR("%s: invalid input\n", __func__); + return; + } + + edid_buf = edid_ctrl->edid_buf; + if (edid_buf[21] && edid_buf[22]) { + edid_ctrl->physical_width = edid_buf[21]; + edid_ctrl->physical_height = edid_buf[22]; + + DEV_DBG("%s: EDID: Horizontal Screen Size = %d cm\n", + __func__, edid_ctrl->physical_width); + DEV_DBG("%s: EDID: Vertical Screen Size = %d cm\n", + __func__, edid_ctrl->physical_height); + } else if (edid_buf[21]) { + edid_ctrl->orientation = LANDSCAPE; + switch (edid_buf[21]) { + case 0x4F: + edid_ctrl->aspect_ratio = HDMI_RES_AR_16_9; + break; + case 0x3D: + edid_ctrl->aspect_ratio = HDMI_RES_AR_16_10; + break; + case 0x22: + edid_ctrl->aspect_ratio = HDMI_RES_AR_4_3; + break; + case 0x1A: + edid_ctrl->aspect_ratio = HDMI_RES_AR_5_4; + break; + } + DEV_DBG("%s: EDID: Landscape Aspect Ratio = %d\n", + __func__, edid_ctrl->aspect_ratio); + } else if (edid_buf[22]) { + edid_ctrl->orientation = PORTRAIT; + switch (edid_buf[22]) { + case 0x4F: + edid_ctrl->aspect_ratio = HDMI_RES_AR_16_9; + break; + case 0x3D: + edid_ctrl->aspect_ratio = HDMI_RES_AR_16_10; + break; + case 0x22: + edid_ctrl->aspect_ratio = HDMI_RES_AR_4_3; + break; + case 0x1A: + edid_ctrl->aspect_ratio = HDMI_RES_AR_5_4; + break; + } + DEV_DBG("%s: EDID: Portrait Aspect Ratio = %d\n", + __func__, edid_ctrl->aspect_ratio); + } else { + pr_debug("%s: Undefined Screen size/Aspect ratio\n", __func__); + edid_ctrl->orientation = 0; + edid_ctrl->physical_width = 0; + edid_ctrl->physical_height = 0; + } +} + static void hdmi_edid_extract_vendor_id(struct hdmi_edid_ctrl *edid_ctrl) { char *vendor_id; @@ -2462,6 +2564,8 @@ int hdmi_edid_parser(void *input) hdmi_edid_extract_vendor_id(edid_ctrl); + hdmi_edid_extract_bdpf(edid_ctrl); + /* EDID_CEA_EXTENSION_FLAG[0x7E] - CEC extension byte */ num_of_cea_blocks = edid_buf[EDID_BLOCK_SIZE - 2]; DEV_DBG("%s: No. of CEA/Extended EDID blocks is [%u]\n", __func__, @@ -2914,6 +3018,30 @@ bool hdmi_edid_is_audio_supported(void *input) return (edid_ctrl->basic_audio_supp || edid_ctrl->adb_size); } +u32 hdmi_edid_get_phys_width(void *input) +{ + struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; + + if (!edid_ctrl) { + DEV_ERR("%s: invalid edid_ctrl data\n", __func__); + return 0; + } + + return (u32)edid_ctrl->physical_width * 10; /* return in mm */ +} + +u32 hdmi_edid_get_phys_height(void *input) +{ + struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; + + if (!edid_ctrl) { + DEV_ERR("%s: invalid edid_ctrl data\n", __func__); + return 0; + } + + return (u32)edid_ctrl->physical_height * 10; /* return in mm */ +} + void hdmi_edid_deinit(void *input) { struct hdmi_edid_ctrl *edid_ctrl = (struct hdmi_edid_ctrl *)input; diff --git a/drivers/video/fbdev/msm/mdss_hdmi_edid.h b/drivers/video/fbdev/msm/mdss_hdmi_edid.h index d258aa9f95bc..9b3b5fa952b4 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_edid.h +++ b/drivers/video/fbdev/msm/mdss_hdmi_edid.h @@ -94,5 +94,7 @@ void hdmi_edid_set_max_pclk_rate(void *input, u32 max_pclk_khz); bool hdmi_edid_is_audio_supported(void *input); u32 hdmi_edid_get_sink_caps_max_tmds_clk(void *input); u8 hdmi_edid_get_colorimetry(void *input); +u32 hdmi_edid_get_phys_width(void *input); +u32 hdmi_edid_get_phys_height(void *input); #endif /* __HDMI_EDID_H__ */ diff --git a/drivers/video/fbdev/msm/mdss_hdmi_tx.c b/drivers/video/fbdev/msm/mdss_hdmi_tx.c index 747c245bf78e..e0fcfec614b5 100644 --- a/drivers/video/fbdev/msm/mdss_hdmi_tx.c +++ b/drivers/video/fbdev/msm/mdss_hdmi_tx.c @@ -23,6 +23,7 @@ #include <linux/hdcp_qseecom.h> #include <linux/msm_mdp.h> #include <linux/msm_ext_display.h> +#include <linux/hdmi.h> #define REG_DUMP 0 @@ -3038,6 +3039,96 @@ static void hdmi_tx_phy_reset(struct hdmi_tx_ctrl *hdmi_ctrl) DSS_REG_W_ND(io, HDMI_PHY_CTRL, val | SW_RESET_PLL); } /* hdmi_tx_phy_reset */ +static u8 calc_infoframe_checksum(u8 *ptr, size_t size) +{ + u8 csum = 0; + size_t i; + + /* compute checksum */ + for (i = 0; i < size; i++) + csum += ptr[i]; + + return 256 - csum; +} + +static u8 hdmi_panel_set_hdr_checksum(struct mdp_hdr_stream *hdr_meta) +{ + u8 *buff; + u8 *ptr; + u32 length; + u32 size; + u32 checksum = 0; + u32 const type_code = 0x87; + u32 const version = 0x01; + u32 const descriptor_id = 0x00; + + /* length of metadata is 26 bytes */ + length = 0x1a; + /* add 4 bytes for the header */ + size = length + HDMI_INFOFRAME_HEADER_SIZE; + + buff = kzalloc(size, GFP_KERNEL); + + if (!buff) { + DEV_ERR("invalid buff\n"); + goto err_alloc; + } + + ptr = buff; + + buff[0] = type_code; + buff[1] = version; + buff[2] = length; + buff[3] = 0; + /* start infoframe payload */ + buff += HDMI_INFOFRAME_HEADER_SIZE; + + buff[0] = hdr_meta->eotf; + buff[1] = descriptor_id; + + buff[2] = hdr_meta->display_primaries_x[0] & 0xff; + buff[3] = hdr_meta->display_primaries_x[0] >> 8; + + buff[4] = hdr_meta->display_primaries_x[1] & 0xff; + buff[5] = hdr_meta->display_primaries_x[1] >> 8; + + buff[6] = hdr_meta->display_primaries_x[2] & 0xff; + buff[7] = hdr_meta->display_primaries_x[2] >> 8; + + buff[8] = hdr_meta->display_primaries_y[0] & 0xff; + buff[9] = hdr_meta->display_primaries_y[0] >> 8; + + buff[10] = hdr_meta->display_primaries_y[1] & 0xff; + buff[11] = hdr_meta->display_primaries_y[1] >> 8; + + buff[12] = hdr_meta->display_primaries_y[2] & 0xff; + buff[13] = hdr_meta->display_primaries_y[2] >> 8; + + buff[14] = hdr_meta->white_point_x & 0xff; + buff[15] = hdr_meta->white_point_x >> 8; + buff[16] = hdr_meta->white_point_y & 0xff; + buff[17] = hdr_meta->white_point_y >> 8; + + buff[18] = hdr_meta->max_luminance & 0xff; + buff[19] = hdr_meta->max_luminance >> 8; + + buff[20] = hdr_meta->min_luminance & 0xff; + buff[21] = hdr_meta->min_luminance >> 8; + + buff[22] = hdr_meta->max_content_light_level & 0xff; + buff[23] = hdr_meta->max_content_light_level >> 8; + + buff[24] = hdr_meta->max_average_light_level & 0xff; + buff[25] = hdr_meta->max_average_light_level >> 8; + + checksum = calc_infoframe_checksum(ptr, size); + + kfree(ptr); + +err_alloc: + return checksum; +} + static void hdmi_panel_set_hdr_infoframe(struct hdmi_tx_ctrl *ctrl) { u32 packet_payload = 0; @@ -3047,8 +3138,10 @@ static void hdmi_panel_set_hdr_infoframe(struct hdmi_tx_ctrl *ctrl) u32 const version = 0x01; u32 const length = 0x1a; u32 const descriptor_id = 0x00; + u8 checksum = 0; struct dss_io_data *io = NULL; + if (!ctrl) { pr_err("%s: invalid input\n", __func__); return; @@ -3069,7 +3162,18 @@ static void hdmi_panel_set_hdr_infoframe(struct hdmi_tx_ctrl *ctrl) packet_header = type_code | (version << 8) | (length << 16); DSS_REG_W(io, HDMI_GENERIC0_HDR, packet_header); - packet_payload = (ctrl->hdr_ctrl.hdr_stream.eotf << 8); + /** + * Checksum is not a mandatory field for + * the HDR infoframe as per CEA-861-3 specification. + * However some HDMI sinks still expect a + * valid checksum to be included as part of + * the infoframe. Hence compute and add + * the checksum to improve sink interoperability + * for our HDR solution on HDMI. + */ + checksum = hdmi_panel_set_hdr_checksum(&ctrl->hdr_ctrl.hdr_stream); + + packet_payload = ((ctrl->hdr_ctrl.hdr_stream.eotf << 8) | checksum); if (hdmi_tx_metadata_type_one(ctrl)) { packet_payload |= (descriptor_id << 16) @@ -3432,6 +3536,9 @@ static int hdmi_tx_power_off(struct hdmi_tx_ctrl *hdmi_ctrl) hdmi_ctrl->panel_power_on = false; hdmi_ctrl->vic = 0; + hdmi_ctrl->use_bt2020 = false; + hdmi_ctrl->curr_hdr_state = HDR_DISABLE; + if (hdmi_ctrl->hpd_off_pending || hdmi_ctrl->panel_suspend) hdmi_tx_hpd_off(hdmi_ctrl); @@ -4125,6 +4232,7 @@ sysfs_err: static int hdmi_tx_evt_handle_check_param(struct hdmi_tx_ctrl *hdmi_ctrl) { struct mdss_panel_info *pinfo = &hdmi_ctrl->panel_data.panel_info; + void *data = NULL; int new_vic = -1; int rc = 0; @@ -4136,6 +4244,10 @@ static int hdmi_tx_evt_handle_check_param(struct hdmi_tx_ctrl *hdmi_ctrl) goto end; } + data = hdmi_tx_get_fd(HDMI_TX_FEAT_EDID); + pinfo->physical_width = hdmi_edid_get_phys_width(data); + pinfo->physical_height = hdmi_edid_get_phys_height(data); + /* * return value of 1 lets mdss know that panel * needs a reconfig due to new resolution and diff --git a/drivers/video/fbdev/msm/mdss_livedisplay.c b/drivers/video/fbdev/msm/mdss_livedisplay.c new file mode 100644 index 000000000000..32f010b6b64a --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_livedisplay.c @@ -0,0 +1,653 @@ +/* + * Copyright (c) 2015 The CyanogenMod Project + * + * 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. + */ + +#include <linux/err.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/slab.h> +#include <linux/sysfs.h> + +#include "mdss_dsi.h" +#include "mdss_fb.h" +#include "mdss_mdp.h" +#include "mdss_mdp_pp.h" +#include "mdss_livedisplay.h" + +/* + * LiveDisplay is the display management service in CyanogenMod. It uses + * various capabilities of the hardware and software in order to + * optimize the experience for ambient conditions and time of day. + * + * This module is initialized by mdss_fb for each panel, and creates + * several new controls in /sys/class/graphics/fbX based on the + * configuration in the devicetree. + * + * cabc: Content Adaptive Backlight Control. Must be configured + * in the panel devicetree. Up to three levels. + * sre: Sunlight Readability Enhancement. Must be configured in + * the panel devicetree. Up to three levels. + * aco: Automatic Contrast Optimization. Must be configured in + * the panel devicetree. Boolean. + * + * hbm: High Brightness Mode. Common for OLED panels. Boolean. + * + * preset: Arbitrary DSI commands, up to 10 may be configured. + * Useful for gamma calibration. + * + * color_enhance: Hardware color enhancement. Must be configured + * in the panel devicetree. Boolean. + */ + +extern void mdss_dsi_panel_cmds_send(struct mdss_dsi_ctrl_pdata *ctrl, + struct dsi_panel_cmds *pcmds, u32 flags); + +static int parse_dsi_cmds(struct mdss_livedisplay_ctx *mlc, + struct dsi_panel_cmds *pcmds, const uint8_t *cmd, int blen) +{ + int len; + char *buf, *bp; + struct dsi_ctrl_hdr *dchdr; + int i, cnt; + + buf = kzalloc(blen, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + memcpy(buf, cmd, blen); + + /* scan dcs commands */ + bp = buf; + len = blen; + cnt = 0; + while (len >= sizeof(*dchdr)) { + dchdr = (struct dsi_ctrl_hdr *)bp; + dchdr->dlen = ntohs(dchdr->dlen); + if (dchdr->dlen > len) { + pr_err("%s: dtsi cmd=%x error, len=%d\n", + __func__, dchdr->dtype, dchdr->dlen); + goto exit_free; + } + bp += sizeof(*dchdr); + len -= sizeof(*dchdr); + bp += dchdr->dlen; + len -= dchdr->dlen; + cnt++; + } + + if (len != 0) { + pr_err("%s: dcs_cmd=%x len=%d error!\n", + __func__, buf[0], blen); + goto exit_free; + } + + pcmds->cmds = kzalloc(cnt * sizeof(struct dsi_cmd_desc), + GFP_KERNEL); + if (!pcmds->cmds) + goto exit_free; + + pcmds->cmd_cnt = cnt; + pcmds->buf = buf; + pcmds->blen = blen; + + bp = buf; + len = blen; + for (i = 0; i < cnt; i++) { + dchdr = (struct dsi_ctrl_hdr *)bp; + len -= sizeof(*dchdr); + bp += sizeof(*dchdr); + pcmds->cmds[i].dchdr = *dchdr; + pcmds->cmds[i].payload = bp; + bp += dchdr->dlen; + len -= dchdr->dlen; + } + + pcmds->link_state = mlc->link_state; + + pr_debug("%s: dcs_cmd=%x len=%d, cmd_cnt=%d link_state=%d\n", __func__, + pcmds->buf[0], pcmds->blen, pcmds->cmd_cnt, pcmds->link_state); + + return 0; + +exit_free: + kfree(buf); + return -ENOMEM; +} + +/* + * Update all or a subset of parameters + */ +int mdss_livedisplay_update(struct mdss_dsi_ctrl_pdata *ctrl_pdata, + int types) +{ + int ret = 0; + struct mdss_panel_info *pinfo = NULL; + struct mdss_livedisplay_ctx *mlc = NULL; + unsigned int len = 0, dlen = 0; + struct dsi_panel_cmds dsi_cmds; + uint8_t cabc_value = 0; + uint8_t *cmd_buf; + + if (ctrl_pdata == NULL) + return -ENODEV; + + pinfo = &(ctrl_pdata->panel_data.panel_info); + if (pinfo == NULL) + return -ENODEV; + + mlc = pinfo->livedisplay; + if (mlc == NULL) + return -ENODEV; + + if (!mlc->caps || !mdss_panel_is_power_on_interactive(pinfo->panel_power_state)) + return 0; + + // First find the length of the command array + if ((mlc->caps & MODE_PRESET) && (types & MODE_PRESET)) + len += mlc->presets_len[mlc->preset]; + + if ((mlc->caps & MODE_COLOR_ENHANCE) && (types & MODE_COLOR_ENHANCE)) + len += mlc->ce_enabled ? mlc->ce_on_cmds_len : mlc->ce_off_cmds_len; + + if ((mlc->caps & MODE_HIGH_BRIGHTNESS) && (types & MODE_HIGH_BRIGHTNESS)) + len += mlc->hbm_enabled ? mlc->hbm_on_cmds_len : mlc->hbm_off_cmds_len; + + if (is_cabc_cmd(types) && is_cabc_cmd(mlc->caps)) { + + // The CABC command on most modern panels is also responsible for + // other features such as SRE and ACO. The register fields are bits + // and are OR'd together and sent in a single DSI command. + if (mlc->cabc_level == CABC_UI) { + if (mlc->unified_cabc_cmds) + cabc_value |= mlc->cabc_ui_value; + else + len += mlc->cabc_ui_cmds_len; + } else if (mlc->cabc_level == CABC_IMAGE) { + if (mlc->unified_cabc_cmds) + cabc_value |= mlc->cabc_image_value; + else + len += mlc->cabc_image_cmds_len; + } else if (mlc->cabc_level == CABC_VIDEO) { + if (mlc->unified_cabc_cmds) + cabc_value |= mlc->cabc_video_value; + else + len += mlc->cabc_video_cmds_len; + } + + if (mlc->sre_level == SRE_WEAK) + cabc_value |= mlc->sre_weak_value; + else if (mlc->sre_level == SRE_MEDIUM) + cabc_value |= mlc->sre_medium_value; + else if (mlc->sre_level == SRE_STRONG) + cabc_value |= mlc->sre_strong_value; + + if (mlc->aco_enabled) + cabc_value |= mlc->aco_value; + + if (cabc_value || mlc->cabc_level == CABC_OFF) + len += mlc->cabc_cmds_len; + + pr_debug("%s cabc=%d sre=%d aco=%d cmd=%d\n", __func__, + mlc->cabc_level, mlc->sre_level, mlc->aco_enabled, + cabc_value); + } + + len += mlc->post_cmds_len; + + if (len == 0) + return 0; + + memset(&dsi_cmds, 0, sizeof(struct dsi_panel_cmds)); + cmd_buf = kzalloc(len + 1, GFP_KERNEL); + if (!cmd_buf) + return -ENOMEM; + + // Build the command as a single chain, preset first + if ((mlc->caps & MODE_PRESET) && (types & MODE_PRESET)) { + memcpy(cmd_buf, mlc->presets[mlc->preset], mlc->presets_len[mlc->preset]); + dlen += mlc->presets_len[mlc->preset]; + } + + // Color enhancement + if ((mlc->caps & MODE_COLOR_ENHANCE) && (types & MODE_COLOR_ENHANCE)) { + if (mlc->ce_enabled) { + memcpy(cmd_buf + dlen, mlc->ce_on_cmds, mlc->ce_on_cmds_len); + dlen += mlc->ce_on_cmds_len; + } else { + memcpy(cmd_buf + dlen, mlc->ce_off_cmds, mlc->ce_off_cmds_len); + dlen += mlc->ce_off_cmds_len; + } + } + + // High brightness mode + if ((mlc->caps & MODE_HIGH_BRIGHTNESS) && (types & MODE_HIGH_BRIGHTNESS)) { + if (mlc->hbm_enabled) { + memcpy(cmd_buf + dlen, mlc->hbm_on_cmds, mlc->hbm_on_cmds_len); + dlen += mlc->hbm_on_cmds_len; + } else { + memcpy(cmd_buf + dlen, mlc->hbm_off_cmds, mlc->hbm_off_cmds_len); + dlen += mlc->hbm_off_cmds_len; + } + } + + // CABC/SRE/ACO features + if (is_cabc_cmd(types) && mlc->cabc_cmds_len) { + if (cabc_value || mlc->cabc_level == CABC_OFF) { + memcpy(cmd_buf + dlen, mlc->cabc_cmds, mlc->cabc_cmds_len); + dlen += mlc->cabc_cmds_len; + // The CABC command parameter is the last value in the sequence + cmd_buf[dlen - 1] = cabc_value; + } + + if (!mlc->unified_cabc_cmds) { + if (mlc->cabc_level == CABC_UI && mlc->cabc_ui_cmds_len) { + memcpy(cmd_buf + dlen, mlc->cabc_ui_cmds, mlc->cabc_ui_cmds_len); + dlen += mlc->cabc_ui_cmds_len; + } else if (mlc->cabc_level == CABC_IMAGE && mlc->cabc_image_cmds_len) { + memcpy(cmd_buf + dlen, mlc->cabc_image_cmds, mlc->cabc_image_cmds_len); + dlen += mlc->cabc_image_cmds_len; + } else if (mlc->cabc_level == CABC_VIDEO && mlc->cabc_video_cmds_len) { + memcpy(cmd_buf + dlen, mlc->cabc_video_cmds, mlc->cabc_video_cmds_len); + dlen += mlc->cabc_video_cmds_len; + } + } + } + + // And the post_cmd, can be used to turn on the panel + if (mlc->post_cmds_len) { + memcpy(cmd_buf + dlen, mlc->post_cmds, mlc->post_cmds_len); + dlen += mlc->post_cmds_len; + } + + // Parse the command and send it + ret = parse_dsi_cmds(mlc, &dsi_cmds, (const uint8_t *)cmd_buf, len); + if (ret == 0) { + mdss_dsi_panel_cmds_send(ctrl_pdata, &dsi_cmds, + CMD_REQ_COMMIT | CMD_CLK_CTRL); + kfree(dsi_cmds.buf); + kfree(dsi_cmds.cmds); + } else { + pr_err("%s: error parsing DSI command! ret=%d", __func__, ret); + } + + kfree(cmd_buf); + + return ret; +} + +int mdss_livedisplay_event(struct msm_fb_data_type *mfd, int types) +{ + int rc = 0; + struct mdss_panel_data *pdata; + + pdata = dev_get_platdata(&mfd->pdev->dev); + if (!pdata) + return -ENODEV; + + do { + if (pdata->event_handler) + rc = pdata->event_handler(pdata, MDSS_EVENT_UPDATE_LIVEDISPLAY, + (void *)(unsigned long) types); + + pdata = pdata->next; + } while (!rc && pdata); + + return rc; +} + +static ssize_t mdss_livedisplay_get_cabc(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_livedisplay_ctx *mlc = get_ctx(mfd); + + return sprintf(buf, "%d\n", mlc->cabc_level); +} + +static ssize_t mdss_livedisplay_set_cabc(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int level = 0; + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_livedisplay_ctx *mlc = get_ctx(mfd); + + sscanf(buf, "%du", &level); + if (level >= CABC_OFF && level < CABC_MAX && + level != mlc->cabc_level) { + mlc->cabc_level = level; + mdss_livedisplay_event(mfd, MODE_CABC); + } + + return count; +} + +static ssize_t mdss_livedisplay_get_sre(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_livedisplay_ctx *mlc = get_ctx(mfd); + + return sprintf(buf, "%d\n", mlc->sre_level); +} + +static ssize_t mdss_livedisplay_set_sre(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int level = 0; + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_livedisplay_ctx *mlc = get_ctx(mfd); + + sscanf(buf, "%du", &level); + if (level >= SRE_OFF && level < SRE_MAX && + level != mlc->sre_level) { + mlc->sre_level = level; + mdss_livedisplay_event(mfd, MODE_SRE); + } + + return count; +} + +static ssize_t mdss_livedisplay_get_hbm(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_livedisplay_ctx *mlc = get_ctx(mfd); + + return sprintf(buf, "%d\n", mlc->hbm_enabled); +} + +static ssize_t mdss_livedisplay_set_hbm(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int value = 0; + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_livedisplay_ctx *mlc = get_ctx(mfd); + + sscanf(buf, "%du", &value); + if ((value == 0 || value == 1) + && value != mlc->hbm_enabled) { + mlc->hbm_enabled = value; + mdss_livedisplay_event(mfd, MODE_HIGH_BRIGHTNESS); + } + + return count; +} + +static ssize_t mdss_livedisplay_get_color_enhance(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_livedisplay_ctx *mlc = get_ctx(mfd); + + return sprintf(buf, "%d\n", mlc->ce_enabled); +} + +static ssize_t mdss_livedisplay_set_color_enhance(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int value = 0; + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_livedisplay_ctx *mlc = get_ctx(mfd); + + sscanf(buf, "%du", &value); + if ((value == 0 || value == 1) + && value != mlc->ce_enabled) { + mlc->ce_enabled = value; + mdss_livedisplay_event(mfd, MODE_COLOR_ENHANCE); + } + + return count; +} + +static ssize_t mdss_livedisplay_get_aco(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_livedisplay_ctx *mlc = get_ctx(mfd); + + return sprintf(buf, "%d\n", mlc->aco_enabled); +} + +static ssize_t mdss_livedisplay_set_aco(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int value = 0; + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_livedisplay_ctx *mlc = get_ctx(mfd); + + sscanf(buf, "%du", &value); + if ((value == 0 || value == 1) + && value != mlc->aco_enabled) { + mlc->aco_enabled = value; + mdss_livedisplay_event(mfd, MODE_AUTO_CONTRAST); + } + + return count; +} + +static ssize_t mdss_livedisplay_get_preset(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_livedisplay_ctx *mlc = get_ctx(mfd); + + return sprintf(buf, "%d\n", mlc->preset); +} + +static ssize_t mdss_livedisplay_set_preset(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + int value = 0; + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_livedisplay_ctx *mlc = get_ctx(mfd); + + sscanf(buf, "%du", &value); + if (value < 0 || value >= mlc->num_presets) + return -EINVAL; + + mlc->preset = value; + mdss_livedisplay_event(mfd, MODE_PRESET); + + return count; +} + +static ssize_t mdss_livedisplay_get_num_presets(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct fb_info *fbi = dev_get_drvdata(dev); + struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par; + struct mdss_livedisplay_ctx *mlc = get_ctx(mfd); + + return sprintf(buf, "%d\n", mlc->num_presets); +} + +static DEVICE_ATTR(cabc, S_IRUGO | S_IWUSR | S_IWGRP, mdss_livedisplay_get_cabc, mdss_livedisplay_set_cabc); +static DEVICE_ATTR(sre, S_IRUGO | S_IWUSR | S_IWGRP, mdss_livedisplay_get_sre, mdss_livedisplay_set_sre); +static DEVICE_ATTR(color_enhance, S_IRUGO | S_IWUSR | S_IWGRP, mdss_livedisplay_get_color_enhance, mdss_livedisplay_set_color_enhance); +static DEVICE_ATTR(aco, S_IRUGO | S_IWUSR | S_IWGRP, mdss_livedisplay_get_aco, mdss_livedisplay_set_aco); +static DEVICE_ATTR(preset, S_IRUGO | S_IWUSR | S_IWGRP, mdss_livedisplay_get_preset, mdss_livedisplay_set_preset); +static DEVICE_ATTR(num_presets, S_IRUGO, mdss_livedisplay_get_num_presets, NULL); +static DEVICE_ATTR(hbm, S_IRUGO | S_IWUSR | S_IWGRP, mdss_livedisplay_get_hbm, mdss_livedisplay_set_hbm); + +int mdss_livedisplay_parse_dt(struct device_node *np, struct mdss_panel_info *pinfo) +{ + int rc = 0, i = 0; + struct mdss_livedisplay_ctx *mlc; + char preset_name[64]; + const char *link_state; + uint32_t tmp = 0; + + if (pinfo == NULL) + return -ENODEV; + + mlc = kzalloc(sizeof(struct mdss_livedisplay_ctx), GFP_KERNEL); + mutex_init(&mlc->lock); + + link_state = of_get_property(np, "cm,mdss-livedisplay-command-state", NULL); + if (link_state && !strcmp(link_state, "dsi_lp_mode")) + mlc->link_state = DSI_LP_MODE; + else + mlc->link_state = DSI_HS_MODE; + + mlc->cabc_cmds = of_get_property(np, + "cm,mdss-livedisplay-cabc-cmd", &mlc->cabc_cmds_len); + + if (mlc->cabc_cmds_len > 0) { + rc = of_property_read_u32(np, "cm,mdss-livedisplay-cabc-ui-value", &tmp); + if (rc == 0) { + // Read unified CABC cmds first + mlc->caps |= MODE_CABC; + mlc->unified_cabc_cmds = true; + mlc->cabc_ui_value = (uint8_t)(tmp & 0xFF); + of_property_read_u32(np, "cm,mdss-livedisplay-cabc-image-value", &tmp); + mlc->cabc_image_value = (uint8_t)(tmp & 0xFF); + of_property_read_u32(np, "cm,mdss-livedisplay-cabc-video-value", &tmp); + mlc->cabc_video_value = (uint8_t)(tmp & 0xFF); + } else { + // If unified CABC cmds don't exist, try independent cmds + mlc->cabc_ui_cmds = of_get_property(np, + "cm,mdss-livedisplay-cabc-ui-cmd", &mlc->cabc_ui_cmds_len); + if (mlc->cabc_ui_cmds_len > 0) { + mlc->caps |= MODE_CABC; + mlc->cabc_image_cmds = of_get_property(np, + "cm,mdss-livedisplay-cabc-image-cmd", &mlc->cabc_image_cmds_len); + mlc->cabc_video_cmds = of_get_property(np, + "cm,mdss-livedisplay-cabc-video-cmd", &mlc->cabc_video_cmds_len); + } + } + rc = of_property_read_u32(np, "cm,mdss-livedisplay-sre-medium-value", &tmp); + if (rc == 0) { + mlc->caps |= MODE_SRE; + mlc->sre_medium_value = (uint8_t)(tmp & 0xFF); + of_property_read_u32(np, "cm,mdss-livedisplay-sre-weak-value", &tmp); + mlc->sre_weak_value = (uint8_t)(tmp & 0xFF); + of_property_read_u32(np, "cm,mdss-livedisplay-sre-strong-value", &tmp); + mlc->sre_strong_value = (uint8_t)(tmp & 0xFF); + } + rc = of_property_read_u32(np, "cm,mdss-livedisplay-aco-value", &tmp); + if (rc == 0) { + mlc->caps |= MODE_AUTO_CONTRAST; + mlc->aco_value = (uint8_t)(tmp & 0xFF); + } + } + + mlc->hbm_on_cmds = of_get_property(np, + "cm,mdss-livedisplay-hbm-on-cmd", &mlc->hbm_on_cmds_len); + if (mlc->hbm_on_cmds_len) { + mlc->hbm_off_cmds = of_get_property(np, + "cm,mdss-livedisplay-hbm-off-cmd", &mlc->hbm_off_cmds_len); + if (mlc->hbm_off_cmds_len) + mlc->caps |= MODE_HIGH_BRIGHTNESS; + } + + mlc->ce_on_cmds = of_get_property(np, + "cm,mdss-livedisplay-color-enhance-on", &mlc->ce_on_cmds_len); + if (mlc->ce_on_cmds_len) { + mlc->ce_off_cmds = of_get_property(np, + "cm,mdss-livedisplay-color-enhance-off", &mlc->ce_off_cmds_len); + if (mlc->ce_off_cmds_len) + mlc->caps |= MODE_COLOR_ENHANCE; + } + + for (i = 0; i < MAX_PRESETS; i++) { + memset(preset_name, 0, sizeof(preset_name)); + snprintf(preset_name, 64, "%s-%d", "cm,mdss-livedisplay-preset", i); + mlc->presets[mlc->num_presets] = of_get_property(np, preset_name, + &mlc->presets_len[mlc->num_presets]); + if (mlc->presets_len[mlc->num_presets] > 0) + mlc->num_presets++; + } + + if (mlc->num_presets) + mlc->caps |= MODE_PRESET; + + mlc->post_cmds = of_get_property(np, + "cm,mdss-livedisplay-post-cmd", &mlc->post_cmds_len); + + pinfo->livedisplay = mlc; + return 0; +} + +int mdss_livedisplay_create_sysfs(struct msm_fb_data_type *mfd) +{ + int rc = 0; + struct mdss_livedisplay_ctx *mlc = get_ctx(mfd); + + if (mlc == NULL) + return 0; + + if (mlc->caps & MODE_CABC) { + rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_cabc.attr); + if (rc) + goto sysfs_err; + } + + if (mlc->caps & MODE_SRE) { + rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_sre.attr); + if (rc) + goto sysfs_err; + } + + if (mlc->caps & MODE_AUTO_CONTRAST) { + rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_aco.attr); + if (rc) + goto sysfs_err; + } + + if (mlc->caps & MODE_COLOR_ENHANCE) { + rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_color_enhance.attr); + if (rc) + goto sysfs_err; + } + + if (mlc->caps & MODE_HIGH_BRIGHTNESS) { + rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_hbm.attr); + if (rc) + goto sysfs_err; + } + + if (mlc->caps & MODE_PRESET) { + rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_preset.attr); + if (rc) + goto sysfs_err; + rc = sysfs_create_file(&mfd->fbi->dev->kobj, &dev_attr_num_presets.attr); + if (rc) + goto sysfs_err; + } + + mlc->mfd = mfd; + + return rc; + +sysfs_err: + pr_err("%s: sysfs creation failed, rc=%d", __func__, rc); + return rc; +} + diff --git a/drivers/video/fbdev/msm/mdss_livedisplay.h b/drivers/video/fbdev/msm/mdss_livedisplay.h new file mode 100644 index 000000000000..58b4f9f6a866 --- /dev/null +++ b/drivers/video/fbdev/msm/mdss_livedisplay.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2015 The CyanogenMod Project + * + * 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_LIVEDISPLAY_H +#define MDSS_LIVEDISPLAY_H + +#include <linux/of.h> +#include <linux/sysfs.h> + +#include "mdss_dsi.h" +#include "mdss_fb.h" + +#define MAX_PRESETS 10 + +struct mdss_livedisplay_ctx { + uint8_t cabc_ui_value; + uint8_t cabc_image_value; + uint8_t cabc_video_value; + uint8_t sre_weak_value; + uint8_t sre_medium_value; + uint8_t sre_strong_value; + uint8_t aco_value; + + const uint8_t *ce_off_cmds; + const uint8_t *ce_on_cmds; + unsigned int ce_off_cmds_len; + unsigned int ce_on_cmds_len; + + const uint8_t *hbm_off_cmds; + const uint8_t *hbm_on_cmds; + unsigned int hbm_off_cmds_len; + unsigned int hbm_on_cmds_len; + + const uint8_t *presets[MAX_PRESETS]; + unsigned int presets_len[MAX_PRESETS]; + + const uint8_t *cabc_cmds; + const uint8_t *cabc_ui_cmds; + const uint8_t *cabc_image_cmds; + const uint8_t *cabc_video_cmds; + unsigned int cabc_cmds_len; + unsigned int cabc_ui_cmds_len; + unsigned int cabc_image_cmds_len; + unsigned int cabc_video_cmds_len; + bool unified_cabc_cmds; + + const uint8_t *post_cmds; + unsigned int post_cmds_len; + + unsigned int preset; + unsigned int cabc_level; + unsigned int sre_level; + bool aco_enabled; + bool ce_enabled; + bool hbm_enabled; + + unsigned int link_state; + + unsigned int num_presets; + unsigned int caps; + + struct msm_fb_data_type *mfd; + + struct mutex lock; +}; + +enum { + CABC_OFF, + CABC_UI, + CABC_IMAGE, + CABC_VIDEO, + CABC_MAX +}; + +enum { + SRE_OFF, + SRE_WEAK, + SRE_MEDIUM, + SRE_STRONG, + SRE_MAX +}; + +enum { + MODE_CABC = 0x01, + MODE_SRE = 0x02, + MODE_AUTO_CONTRAST = 0x04, + MODE_COLOR_ENHANCE = 0x08, + MODE_PRESET = 0x10, + MODE_HIGH_BRIGHTNESS = 0x20, + MODE_UPDATE_ALL = 0xFF, +}; + +int mdss_livedisplay_update(struct mdss_dsi_ctrl_pdata *ctrl_pdata, int types); +int mdss_livedisplay_parse_dt(struct device_node *np, struct mdss_panel_info *pinfo); +int mdss_livedisplay_create_sysfs(struct msm_fb_data_type *mfd); +int mdss_livedisplay_event(struct msm_fb_data_type *mfd, int types); + +static inline bool is_cabc_cmd(unsigned int value) +{ + return (value & MODE_CABC) || (value & MODE_SRE) || (value & MODE_AUTO_CONTRAST); +} + +static inline struct mdss_livedisplay_ctx* get_ctx(struct msm_fb_data_type *mfd) +{ + return mfd->panel_info->livedisplay; +} + +static inline struct mdss_dsi_ctrl_pdata* get_ctrl(struct msm_fb_data_type *mfd) +{ + struct mdss_panel_data *pdata = dev_get_platdata(&mfd->pdev->dev); + return container_of(pdata, struct mdss_dsi_ctrl_pdata, panel_data); +} + +#endif diff --git a/drivers/video/fbdev/msm/mdss_mdp.c b/drivers/video/fbdev/msm/mdss_mdp.c index 98172233b5aa..84be290a0369 100644 --- a/drivers/video/fbdev/msm/mdss_mdp.c +++ b/drivers/video/fbdev/msm/mdss_mdp.c @@ -4854,7 +4854,7 @@ struct mdss_panel_cfg *mdss_panel_intf_type(int intf_val) } EXPORT_SYMBOL(mdss_panel_intf_type); -struct irq_info *mdss_intr_line() +struct irq_info *mdss_intr_line(void) { return mdss_mdp_hw.irq_info; } diff --git a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c index 3761fa4af0eb..caa910db508c 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_intf_video.c +++ b/drivers/video/fbdev/msm/mdss_mdp_intf_video.c @@ -1236,7 +1236,7 @@ static int mdss_mdp_video_wait4comp(struct mdss_mdp_ctl *ctl, void *arg) if (rc == 0) { pr_warn("vsync wait timeout %d, fallback to poll mode\n", ctl->num); - ctx->polling_en++; + ctx->polling_en = true; rc = mdss_mdp_video_pollwait(ctl); } else { rc = 0; diff --git a/drivers/video/fbdev/msm/mdss_mdp_overlay.c b/drivers/video/fbdev/msm/mdss_mdp_overlay.c index daeed5d45072..012870048050 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_overlay.c +++ b/drivers/video/fbdev/msm/mdss_mdp_overlay.c @@ -5254,6 +5254,7 @@ static int __handle_overlay_prepare(struct msm_fb_data_type *mfd, sorted_ovs = kzalloc(num_ovs * sizeof(*ip_ovs), GFP_KERNEL); if (!sorted_ovs) { pr_err("error allocating ovlist mem\n"); + mutex_unlock(&mdp5_data->ov_lock); return -ENOMEM; } memcpy(sorted_ovs, ip_ovs, num_ovs * sizeof(*ip_ovs)); @@ -5261,6 +5262,7 @@ static int __handle_overlay_prepare(struct msm_fb_data_type *mfd, if (ret) { pr_err("src_split_sort failed. ret=%d\n", ret); kfree(sorted_ovs); + mutex_unlock(&mdp5_data->ov_lock); return ret; } } diff --git a/drivers/video/fbdev/msm/mdss_mdp_pp.c b/drivers/video/fbdev/msm/mdss_mdp_pp.c index edc154b2b0c6..f2afd04eaf29 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_pp.c +++ b/drivers/video/fbdev/msm/mdss_mdp_pp.c @@ -4129,7 +4129,8 @@ int mdss_mdp_igc_lut_config(struct msm_fb_data_type *mfd, if (config->len != IGC_LUT_ENTRIES) { pr_err("invalid len for IGC table for read %d\n", config->len); - return -EINVAL; + ret = -EINVAL; + goto igc_config_exit; } ret = pp_get_dspp_num(disp_num, &dspp_num); if (ret) { @@ -4195,7 +4196,8 @@ clock_off: if (config->len != IGC_LUT_ENTRIES) { pr_err("invalid len for IGC table for write %d\n", config->len); - return -EINVAL; + ret = -EINVAL; + goto igc_config_exit; } if (copy_from_kernel) { memcpy(&mdss_pp_res->igc_lut_c0c1[disp_num][0], diff --git a/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c b/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c index b55a6824ffc1..b085bd21bee2 100644 --- a/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c +++ b/drivers/video/fbdev/msm/mdss_mdp_splash_logo.c @@ -418,8 +418,11 @@ static int mdss_mdp_splash_kickoff(struct msm_fb_data_type *mfd, } req = kzalloc(sizeof(struct mdp_overlay), GFP_KERNEL); - if (!req) - return -ENOMEM; + if (!req) { + pr_err("fail allocate memory\n"); + ret = -ENOMEM; + goto end; + } /* * use single pipe for diff --git a/drivers/video/fbdev/msm/mdss_panel.h b/drivers/video/fbdev/msm/mdss_panel.h index bc2513296fd8..059bc6ba4a76 100644 --- a/drivers/video/fbdev/msm/mdss_panel.h +++ b/drivers/video/fbdev/msm/mdss_panel.h @@ -312,6 +312,7 @@ enum mdss_intf_events { MDSS_EVENT_AVR_MODE, MDSS_EVENT_REGISTER_CLAMP_HANDLER, MDSS_EVENT_DSI_DYNAMIC_BITCLK, + MDSS_EVENT_UPDATE_LIVEDISPLAY, MDSS_EVENT_MAX, }; @@ -764,6 +765,8 @@ struct mdss_dsi_dual_pu_roi { bool enabled; }; +struct mdss_livedisplay_ctx; + struct mdss_panel_hdr_properties { bool hdr_enabled; @@ -926,6 +929,8 @@ struct mdss_panel_info { */ u32 adjust_timer_delay_ms; + struct mdss_livedisplay_ctx *livedisplay; + /* debugfs structure for the panel */ struct mdss_panel_debugfs_info *debugfs_info; diff --git a/drivers/video/fbdev/msm/mdss_util.c b/drivers/video/fbdev/msm/mdss_util.c index 65941601cfdc..d6c462e04dfd 100644 --- a/drivers/video/fbdev/msm/mdss_util.c +++ b/drivers/video/fbdev/msm/mdss_util.c @@ -232,7 +232,7 @@ struct mdss_util_intf mdss_util = { .mdp_probe_done = false }; -struct mdss_util_intf *mdss_get_util_intf() +struct mdss_util_intf *mdss_get_util_intf(void) { return &mdss_util; } diff --git a/drivers/video/fbdev/msm/msm_mdss_io_8974.c b/drivers/video/fbdev/msm/msm_mdss_io_8974.c index 922c4440ba82..000beebe0375 100644 --- a/drivers/video/fbdev/msm/msm_mdss_io_8974.c +++ b/drivers/video/fbdev/msm/msm_mdss_io_8974.c @@ -1321,16 +1321,16 @@ static void mdss_dsi_phy_regulator_ctrl(struct mdss_dsi_ctrl_pdata *ctrl, mdss_dsi_20nm_phy_regulator_enable(ctrl); break; default: - /* - * For dual dsi case, do not reconfigure dsi phy - * regulator if the other dsi controller is still - * active. - */ - if (!mdss_dsi_is_hw_config_dual(sdata) || - (other_ctrl && (!other_ctrl->is_phyreg_enabled - || other_ctrl->mmss_clamp))) - mdss_dsi_28nm_phy_regulator_enable(ctrl); - break; + /* + * For dual dsi case, do not reconfigure dsi phy + * regulator if the other dsi controller is still + * active. + */ + if (!mdss_dsi_is_hw_config_dual(sdata) || + (other_ctrl && (!other_ctrl->is_phyreg_enabled + || other_ctrl->mmss_clamp))) + mdss_dsi_28nm_phy_regulator_enable(ctrl); + break; } } ctrl->is_phyreg_enabled = 1; diff --git a/drivers/video/fbdev/neofb.c b/drivers/video/fbdev/neofb.c index db023a97d1ea..e243254a5721 100644 --- a/drivers/video/fbdev/neofb.c +++ b/drivers/video/fbdev/neofb.c @@ -1820,6 +1820,7 @@ static int neo_scan_monitor(struct fb_info *info) #else printk(KERN_ERR "neofb: Only 640x480, 800x600/480 and 1024x768 panels are currently supported\n"); + kfree(info->monspecs.modedb); return -1; #endif default: diff --git a/drivers/video/fbdev/omap2/dss/dss.c b/drivers/video/fbdev/omap2/dss/dss.c index 9200a8668b49..a57c3a5f4bf8 100644 --- a/drivers/video/fbdev/omap2/dss/dss.c +++ b/drivers/video/fbdev/omap2/dss/dss.c @@ -843,7 +843,7 @@ static const struct dss_features omap34xx_dss_feats = { }; static const struct dss_features omap3630_dss_feats = { - .fck_div_max = 32, + .fck_div_max = 31, .dss_fck_multiplier = 1, .parent_clk_name = "dpll4_ck", .dpi_select_source = &dss_dpi_select_source_omap2_omap3, diff --git a/drivers/video/fbdev/omap2/dss/omapdss-boot-init.c b/drivers/video/fbdev/omap2/dss/omapdss-boot-init.c index 8b6f6d5fdd68..43186fa8a13c 100644 --- a/drivers/video/fbdev/omap2/dss/omapdss-boot-init.c +++ b/drivers/video/fbdev/omap2/dss/omapdss-boot-init.c @@ -194,7 +194,7 @@ static int __init omapdss_boot_init(void) dss = of_find_matching_node(NULL, omapdss_of_match); if (dss == NULL || !of_device_is_available(dss)) - return 0; + goto put_node; omapdss_walk_device(dss, true); @@ -221,6 +221,8 @@ static int __init omapdss_boot_init(void) kfree(n); } +put_node: + of_node_put(dss); return 0; } diff --git a/drivers/video/fbdev/pvr2fb.c b/drivers/video/fbdev/pvr2fb.c index 750a384bf191..1a015a6b682e 100644 --- a/drivers/video/fbdev/pvr2fb.c +++ b/drivers/video/fbdev/pvr2fb.c @@ -1029,6 +1029,8 @@ static int __init pvr2fb_setup(char *options) if (!options || !*options) return 0; + cable_arg[0] = output_arg[0] = 0; + while ((this_opt = strsep(&options, ","))) { if (!*this_opt) continue; diff --git a/drivers/video/fbdev/riva/fbdev.c b/drivers/video/fbdev/riva/fbdev.c index f1ad2747064b..6e5e29fe13db 100644 --- a/drivers/video/fbdev/riva/fbdev.c +++ b/drivers/video/fbdev/riva/fbdev.c @@ -1088,6 +1088,9 @@ static int rivafb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) int mode_valid = 0; NVTRACE_ENTER(); + if (!var->pixclock) + return -EINVAL; + switch (var->bits_per_pixel) { case 1 ... 8: var->red.offset = var->green.offset = var->blue.offset = 0; diff --git a/drivers/video/fbdev/sis/init.c b/drivers/video/fbdev/sis/init.c index dfe3eb769638..fde27feae5d0 100644 --- a/drivers/video/fbdev/sis/init.c +++ b/drivers/video/fbdev/sis/init.c @@ -2428,6 +2428,11 @@ SiS_SetCRT1FIFO_630(struct SiS_Private *SiS_Pr, unsigned short ModeNo, i = 0; + if (SiS_Pr->ChipType == SIS_730) + queuedata = &FQBQData730[0]; + else + queuedata = &FQBQData[0]; + if(ModeNo > 0x13) { /* Get VCLK */ @@ -2445,12 +2450,6 @@ SiS_SetCRT1FIFO_630(struct SiS_Private *SiS_Pr, unsigned short ModeNo, /* Get half colordepth */ colorth = colortharray[(SiS_Pr->SiS_ModeType - ModeEGA)]; - if(SiS_Pr->ChipType == SIS_730) { - queuedata = &FQBQData730[0]; - } else { - queuedata = &FQBQData[0]; - } - do { templ = SiS_CalcDelay2(SiS_Pr, queuedata[i]) * VCLK * colorth; diff --git a/drivers/video/fbdev/sis/init301.c b/drivers/video/fbdev/sis/init301.c index 20f7234e809e..c43b951cfb25 100644 --- a/drivers/video/fbdev/sis/init301.c +++ b/drivers/video/fbdev/sis/init301.c @@ -522,9 +522,7 @@ SiS_PanelDelay(struct SiS_Private *SiS_Pr, unsigned short DelayTime) SiS_DDC2Delay(SiS_Pr, 0x4000); } - } else if((SiS_Pr->SiS_IF_DEF_LVDS == 1) /* || - (SiS_Pr->SiS_CustomT == CUT_COMPAQ1280) || - (SiS_Pr->SiS_CustomT == CUT_CLEVO1400) */ ) { /* 315 series, LVDS; Special */ + } else if (SiS_Pr->SiS_IF_DEF_LVDS == 1) { /* 315 series, LVDS; Special */ if(SiS_Pr->SiS_IF_DEF_CH70xx == 0) { PanelID = SiS_GetReg(SiS_Pr->SiS_P3d4,0x36); diff --git a/drivers/video/fbdev/sm712fb.c b/drivers/video/fbdev/sm712fb.c index 589ac7e75413..c8ee58e0ae3e 100644 --- a/drivers/video/fbdev/sm712fb.c +++ b/drivers/video/fbdev/sm712fb.c @@ -1428,6 +1428,8 @@ static int smtc_map_smem(struct smtcfb_info *sfb, static void smtc_unmap_smem(struct smtcfb_info *sfb) { if (sfb && sfb->fb->screen_base) { + if (sfb->chip_id == 0x720) + sfb->fb->screen_base -= 0x00200000; iounmap(sfb->fb->screen_base); sfb->fb->screen_base = NULL; } diff --git a/drivers/video/fbdev/vga16fb.c b/drivers/video/fbdev/vga16fb.c index 283d335a759f..06cee2a40a9b 100644 --- a/drivers/video/fbdev/vga16fb.c +++ b/drivers/video/fbdev/vga16fb.c @@ -243,7 +243,7 @@ static void vga16fb_update_fix(struct fb_info *info) } static void vga16fb_clock_chip(struct vga16fb_par *par, - unsigned int pixclock, + unsigned int *pixclock, const struct fb_info *info, int mul, int div) { @@ -259,14 +259,14 @@ static void vga16fb_clock_chip(struct vga16fb_par *par, { 0 /* bad */, 0x00, 0x00}}; int err; - pixclock = (pixclock * mul) / div; + *pixclock = (*pixclock * mul) / div; best = vgaclocks; - err = pixclock - best->pixclock; + err = *pixclock - best->pixclock; if (err < 0) err = -err; for (ptr = vgaclocks + 1; ptr->pixclock; ptr++) { int tmp; - tmp = pixclock - ptr->pixclock; + tmp = *pixclock - ptr->pixclock; if (tmp < 0) tmp = -tmp; if (tmp < err) { err = tmp; @@ -275,7 +275,7 @@ static void vga16fb_clock_chip(struct vga16fb_par *par, } par->misc |= best->misc; par->clkdiv = best->seq_clock_mode; - pixclock = (best->pixclock * div) / mul; + *pixclock = (best->pixclock * div) / mul; } #define FAIL(X) return -EINVAL @@ -497,10 +497,10 @@ static int vga16fb_check_var(struct fb_var_screeninfo *var, if (mode & MODE_8BPP) /* pixel clock == vga clock / 2 */ - vga16fb_clock_chip(par, var->pixclock, info, 1, 2); + vga16fb_clock_chip(par, &var->pixclock, info, 1, 2); else /* pixel clock == vga clock */ - vga16fb_clock_chip(par, var->pixclock, info, 1, 1); + vga16fb_clock_chip(par, &var->pixclock, info, 1, 1); var->red.offset = var->green.offset = var->blue.offset = var->transp.offset = 0; @@ -1122,7 +1122,7 @@ static void vga_8planes_imageblit(struct fb_info *info, const struct fb_image *i char oldop = setop(0); char oldsr = setsr(0); char oldmask = selectmask(); - const char *cdat = image->data; + const unsigned char *cdat = image->data; u32 dx = image->dx; char __iomem *where; int y; diff --git a/drivers/video/fbdev/w100fb.c b/drivers/video/fbdev/w100fb.c index 10951c82f6ed..7bd4c27cfb14 100644 --- a/drivers/video/fbdev/w100fb.c +++ b/drivers/video/fbdev/w100fb.c @@ -583,6 +583,7 @@ static void w100fb_restore_vidmem(struct w100fb_par *par) memsize=par->mach->mem->size; memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), par->saved_extmem, memsize); vfree(par->saved_extmem); + par->saved_extmem = NULL; } if (par->saved_intmem) { memsize=MEM_INT_SIZE; @@ -591,6 +592,7 @@ static void w100fb_restore_vidmem(struct w100fb_par *par) else memcpy_toio(remapped_fbuf + (W100_FB_BASE-MEM_WINDOW_BASE), par->saved_intmem, memsize); vfree(par->saved_intmem); + par->saved_intmem = NULL; } } |