From 23a7f64be1c3770e95f79bf1b21798a11751c268 Mon Sep 17 00:00:00 2001 From: Krishna Chaitanya Parimi Date: Fri, 14 Nov 2014 17:45:25 +0530 Subject: [PATCH] msm: mdss: Add gamma correction support in mdp3 Add gamma correction support in mdp3 driver via MSMFB_MDP_PP ioctl. The ioctl has been extended to program luts corresponding to histogram based outputs and gamma correction based on flag. The gain corresponding to gamma correction and the histogram based output are combined in driver and applied to hardware. Change-Id: Ie875b4a9f4f4c752ad9a811ad68aff1c5110d57b Signed-off-by: Krishna Chaitanya Parimi --- drivers/video/fbdev/msm/mdp3.c | 3 + drivers/video/fbdev/msm/mdp3_ctrl.c | 360 ++++++++++++++++++-- drivers/video/fbdev/msm/mdp3_ctrl.h | 4 + drivers/video/fbdev/msm/mdp3_dma.c | 10 +- drivers/video/fbdev/msm/mdp3_dma.h | 12 +- drivers/video/fbdev/msm/mdss_compat_utils.c | 57 ++++ drivers/video/fbdev/msm/mdss_compat_utils.h | 7 + include/uapi/linux/msm_mdp.h | 17 + 8 files changed, 432 insertions(+), 38 deletions(-) diff --git a/drivers/video/fbdev/msm/mdp3.c b/drivers/video/fbdev/msm/mdp3.c index beb0595d0008..1e3ab52a849b 100644 --- a/drivers/video/fbdev/msm/mdp3.c +++ b/drivers/video/fbdev/msm/mdp3.c @@ -905,6 +905,9 @@ static int mdp3_hw_init(void) mdp3_res->dma[i].capability = MDP3_DMA_CAP_ALL; mdp3_res->dma[i].in_use = 0; mdp3_res->dma[i].available = 1; + mdp3_res->dma[i].lut_sts = 0; + mdp3_res->dma[i].hist_cmap = NULL; + mdp3_res->dma[i].gc_cmap = NULL; } mdp3_res->dma[MDP3_DMA_S].capability = MDP3_DMA_CAP_DITHER; mdp3_res->dma[MDP3_DMA_E].available = 0; diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.c b/drivers/video/fbdev/msm/mdp3_ctrl.c index ef7d46bd363d..13a2c6f9d82a 100644 --- a/drivers/video/fbdev/msm/mdp3_ctrl.c +++ b/drivers/video/fbdev/msm/mdp3_ctrl.c @@ -35,6 +35,30 @@ static int mdp3_histogram_stop(struct mdp3_session_data *session, static int mdp3_ctrl_clk_enable(struct msm_fb_data_type *mfd, int enable); static int mdp3_ctrl_vsync_enable(struct msm_fb_data_type *mfd, int enable); static int mdp3_ctrl_get_intf_type(struct msm_fb_data_type *mfd); +static int mdp3_ctrl_lut_read(struct msm_fb_data_type *mfd, + struct mdp_rgb_lut_data *cfg); +static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd, + struct mdp_rgb_lut_data *cfg); + +u32 mdp_lut_inverse16[MDP_LUT_SIZE] = { +0, 65536, 32768, 21845, 16384, 13107, 10923, 9362, 8192, 7282, 6554, 5958, +5461, 5041, 4681, 4369, 4096, 3855, 3641, 3449, 3277, 3121, 2979, 2849, 2731, +2621, 2521, 2427, 2341, 2260, 2185, 2114, 2048, 1986, 1928, 1872, 1820, 1771, +1725, 1680, 1638, 1598, 1560, 1524, 1489, 1456, 1425, 1394, 1365, 1337, 1311, +1285, 1260, 1237, 1214, 1192, 1170, 1150, 1130, 1111, 1092, 1074, 1057, 1040, +1024, 1008, 993, 978, 964, 950, 936, 923, 910, 898, 886, 874, 862, 851, 840, +830, 819, 809, 799, 790, 780, 771, 762, 753, 745, 736, 728, 720, 712, 705, 697, +690, 683, 676, 669, 662, 655, 649, 643, 636, 630, 624, 618, 612, 607, 601, 596, +590, 585, 580, 575, 570, 565, 560, 555, 551, 546, 542, 537, 533, 529, 524, 520, +516, 512, 508, 504, 500, 496, 493, 489, 485, 482, 478, 475, 471, 468, 465, 462, +458, 455, 452, 449, 446, 443, 440, 437, 434, 431, 428, 426, 423, 420, 417, 415, +412, 410, 407, 405, 402, 400, 397, 395, 392, 390, 388, 386, 383, 381, 379, 377, +374, 372, 370, 368, 366, 364, 362, 360, 358, 356, 354, 352, 350, 349, 347, 345, +343, 341, 340, 338, 336, 334, 333, 331, 329, 328, 326, 324, 323, 321, 320, 318, +317, 315, 314, 312, 311, 309, 308, 306, 305, 303, 302, 301, 299, 298, 297, 295, +294, 293, 291, 290, 289, 287, 286, 285, 284, 282, 281, 280, 279, 278, 277, 275, +274, 273, 272, 271, 270, 269, 267, 266, 265, 264, 263, 262, 261, 260, 259, 258, +257}; static void mdp3_bufq_init(struct mdp3_buffer_queue *bufq) { @@ -1553,6 +1577,7 @@ static int mdp3_pp_ioctl(struct msm_fb_data_type *mfd, { int ret = -EINVAL; struct msmfb_mdp_pp mdp_pp; + struct mdp_lut_cfg_data *lut; struct mdp3_session_data *mdp3_session; if (!mfd || !mfd->mdp.private1) @@ -1583,6 +1608,21 @@ static int mdp3_pp_ioctl(struct msm_fb_data_type *mfd, ret = mdp3_csc_config(mdp3_session, &(mdp_pp.data.csc_cfg_data)); break; + case mdp_op_lut_cfg: + lut = &mdp_pp.data.lut_cfg_data; + if (lut->lut_type != mdp_lut_rgb) { + pr_err("Lut type %d is not supported", lut->lut_type); + return -EINVAL; + } + if (lut->data.rgb_lut_data.flags & MDP_PP_OPS_READ) + ret = mdp3_ctrl_lut_read(mfd, + &(lut->data.rgb_lut_data)); + else + ret = mdp3_ctrl_lut_update(mfd, + &(lut->data.rgb_lut_data)); + if (ret) + pr_err("%s: invalid rgb lut data\n", __func__); + break; default: pr_err("Unsupported request to MDP_PP IOCTL.\n"); @@ -1640,55 +1680,277 @@ static int mdp3_histo_ioctl(struct msm_fb_data_type *mfd, u32 cmd, return ret; } +static int mdp3_validate_lut_data(struct fb_cmap *cmap) +{ + u32 i = 0; + + if (!cmap || !cmap->red || !cmap->green || !cmap->blue) { + pr_err("Invalid arguments!\n"); + return -EINVAL; + } + + for (i = 0; i < MDP_LUT_SIZE; i++) { + if (cmap->red[i] > 0xFF || cmap->green[i] > 0xFF || + cmap->blue[i] > 0xFF) { + pr_err("LUT value over 255 (limit) at %d index\n", i); + return -EINVAL; + } + } + + return 0; +} + +static int mdp3_alloc_lut_buffer(struct platform_device *pdev, void **cmap) +{ + struct fb_cmap *map; + + map = devm_kzalloc(&pdev->dev, sizeof(struct fb_cmap), GFP_KERNEL); + if (map == NULL) { + pr_err("Failed memory allocation for cmap\n"); + return -ENOMEM; + } + memset(map, 0, sizeof(struct fb_cmap)); + + map->red = devm_kzalloc(&pdev->dev, MDP_LUT_SIZE * sizeof(u16), + GFP_KERNEL); + if (map->red == NULL) { + pr_err("Failed cmap allocation for red\n"); + goto exit_red; + } + memset(map->red, 0, sizeof(u16) * MDP_LUT_SIZE); + + map->green = devm_kzalloc(&pdev->dev, MDP_LUT_SIZE * sizeof(u16), + GFP_KERNEL); + if (map->green == NULL) { + pr_err("Failed cmap allocation for green\n"); + goto exit_green; + } + memset(map->green, 0, sizeof(u16) * MDP_LUT_SIZE); + + map->blue = devm_kzalloc(&pdev->dev, MDP_LUT_SIZE * sizeof(u16), + GFP_KERNEL); + if (map->blue == NULL) { + pr_err("Failed cmap allocation for blue\n"); + goto exit_blue; + } + memset(map->blue, 0, sizeof(u16) * MDP_LUT_SIZE); + + *cmap = map; + return 0; +exit_blue: + devm_kfree(&pdev->dev, map->green); +exit_green: + devm_kfree(&pdev->dev, map->red); +exit_red: + devm_kfree(&pdev->dev, map); + return -ENOMEM; +} + +static void mdp3_free_lut_buffer(struct platform_device *pdev, void **cmap) +{ + struct fb_cmap *map = (struct fb_cmap *)(*cmap); + + if (map == NULL) + return; + + devm_kfree(&pdev->dev, map->blue); + map->blue = NULL; + devm_kfree(&pdev->dev, map->green); + map->green = NULL; + devm_kfree(&pdev->dev, map->red); + map->red = NULL; + devm_kfree(&pdev->dev, map); + map = NULL; +} + +static void mdp3_lut_combine_gain(struct fb_cmap *cmap, struct mdp3_dma *dma) +{ + int i = 0; + u32 r = 0, g = 0, b = 0; + + if (!cmap || !dma || !dma->gc_cmap || !dma->hist_cmap || + !dma->gc_cmap->red || !dma->gc_cmap->green || + !dma->gc_cmap->blue || !dma->hist_cmap->red || + !dma->hist_cmap->green || !dma->hist_cmap->blue) { + pr_err("Invalid params\n"); + return; + } + + for (i = 1; i < MDP_LUT_SIZE; i++) { + r = MIN(dma->gc_cmap->red[i] * dma->hist_cmap->red[i] * + mdp_lut_inverse16[i], 0xFF0000); + g = MIN(dma->gc_cmap->green[i] * dma->hist_cmap->green[i] * + mdp_lut_inverse16[i], 0xFF0000); + b = MIN(dma->gc_cmap->blue[i] * dma->hist_cmap->blue[i] * + mdp_lut_inverse16[i], 0xFF0000); + + cmap->red[i] = (r >> 16) & 0xFF; + cmap->green[i] = (g >> 16) & 0xFF; + cmap->blue[i] = (b >> 16) & 0xFF; + } +} + static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd, - struct fb_cmap *cmap) + struct mdp_rgb_lut_data *cfg) { int rc = 0; + bool data_validated = false; struct mdp3_session_data *mdp3_session = mfd->mdp.private1; + struct mdp3_dma *dma; struct mdp3_dma_lut_config lut_config; - struct mdp3_dma_lut lut; - static u16 r[MDP_LUT_SIZE]; - static u16 g[MDP_LUT_SIZE]; - static u16 b[MDP_LUT_SIZE]; + struct fb_cmap *cmap; - if (!mdp3_session->dma->config_lut) + dma = mdp3_session->dma; + + if (!dma->config_lut) { + pr_err("Config LUT not defined!\n"); return -EINVAL; + } - if (cmap->start > MDP_LUT_SIZE || cmap->len > MDP_LUT_SIZE || - (cmap->start + cmap->len > MDP_LUT_SIZE)) { - pr_err("mdp3_ctrl_lut_update invalid arguments\n"); + if (cfg->cmap.start + cfg->cmap.len > MDP_LUT_SIZE) { + pr_err("Invalid arguments\n"); return -EINVAL; } - rc = copy_from_user(r + cmap->start, - cmap->red, sizeof(u16)*cmap->len); - rc |= copy_from_user(g + cmap->start, - cmap->green, sizeof(u16)*cmap->len); - rc |= copy_from_user(b + cmap->start, - cmap->blue, sizeof(u16)*cmap->len); - if (rc) - return rc; + rc = mdp3_alloc_lut_buffer(mfd->pdev, (void **) &cmap); + if (rc) { + pr_err("No memory\n"); + return -ENOMEM; + } + + mutex_lock(&mdp3_session->pp_lock); + rc = copy_from_user(cmap->red + cfg->cmap.start, + cfg->cmap.red, sizeof(u16) * cfg->cmap.len); + rc |= copy_from_user(cmap->green + cfg->cmap.start, + cfg->cmap.green, sizeof(u16) * cfg->cmap.len); + rc |= copy_from_user(cmap->blue + cfg->cmap.start, + cfg->cmap.blue, sizeof(u16) * cfg->cmap.len); + if (rc) { + pr_err("Copying user data failed!\n"); + goto exit_err; + } + + switch (cfg->lut_type) { + case mdp_rgb_lut_gc: + if (cfg->flags & MDP_PP_OPS_DISABLE) { + if (dma->lut_sts & MDP3_LUT_GC_EN) + /* Free GC cmap cache since disabled */ + mdp3_free_lut_buffer(mfd->pdev, + (void **)&dma->gc_cmap); + dma->lut_sts &= ~MDP3_LUT_GC_EN; + } else if (!(dma->lut_sts & MDP3_LUT_GC_EN)) { + /* Check if values sent are valid */ + rc = mdp3_validate_lut_data(cmap); + if (rc) { + pr_err("Invalid GC LUT data\n"); + goto exit_err; + } + data_validated = true; + + /* Allocate GC cmap cache to store values */ + rc = mdp3_alloc_lut_buffer(mfd->pdev, + (void **)&dma->gc_cmap); + if (rc) { + pr_err("GC LUT config failed\n"); + goto exit_err; + } + dma->lut_sts |= MDP3_LUT_GC_EN; + } + /* + * Copy the GC values from userspace to maintain the + * correct values user intended to program in cache. + * The values programmed in HW might factor in presence + * of other LUT modifying features hence can be + * different from these user given values. + */ + if (dma->lut_sts & MDP3_LUT_GC_EN) { + /* Validate LUT data if not yet validated */ + if (!data_validated) { + rc = mdp3_validate_lut_data(cmap); + if (rc) { + pr_err("Invalid GC LUT data\n"); + goto exit_err; + } + } + memcpy(dma->gc_cmap, cmap, + sizeof(struct fb_cmap)); + } + break; + case mdp_rgb_lut_hist: + if (cfg->flags & MDP_PP_OPS_DISABLE) { + if (dma->lut_sts & MDP3_LUT_HIST_EN) + /* Free HIST cmap cache since disabled */ + mdp3_free_lut_buffer(mfd->pdev, + (void **)&dma->hist_cmap); + dma->lut_sts &= ~MDP3_LUT_HIST_EN; + } else if (!(dma->lut_sts & MDP3_LUT_HIST_EN)) { + /* Check if values sent are valid */ + rc = mdp3_validate_lut_data(cmap); + if (rc) { + pr_err("Invalid HIST LUT data\n"); + goto exit_err; + } + data_validated = true; + + /* Allocate HIST cmap cache to store values */ + rc = mdp3_alloc_lut_buffer(mfd->pdev, + (void **)&dma->hist_cmap); + if (rc) { + pr_err("HIST LUT config failed\n"); + goto exit_err; + } + dma->lut_sts |= MDP3_LUT_HIST_EN; + } + /* + * Copy the HIST LUT values from userspace to maintain + * correct values user intended to program in cache. + * The values programmed in HW might factor in presence + * of other LUT modifying features hence can be + * different from these user given values. + */ + if (dma->lut_sts & MDP3_LUT_HIST_EN) { + /* Validate LUT data if not yet validated */ + if (!data_validated) { + rc = mdp3_validate_lut_data(cmap); + if (rc) { + pr_err("Invalid H LUT data\n"); + goto exit_err; + } + } + memcpy(dma->hist_cmap, cmap, + sizeof(struct fb_cmap)); + } + break; + default: + pr_err("Invalid lut type: %u\n", cfg->lut_type); + rc = -EINVAL; + goto exit_err; + } + + /* + * In case both GC LUT and HIST LUT need to be programmed the gains + * of each the individual LUTs need to be applied onto a single LUT + * and applied in HW + */ + if (dma->lut_sts & MDP3_LUT_HIST_GC_EN) + mdp3_lut_combine_gain(cmap, dma); lut_config.lut_enable = 7; lut_config.lut_sel = mdp3_session->lut_sel; lut_config.lut_position = 0; lut_config.lut_dirty = true; - /* In HW the order is color0 = g, color1 = r and color2 = b*/ - lut.color0_lut = g; - lut.color1_lut = r; - lut.color2_lut = b; mutex_lock(&mdp3_session->lock); if (!mdp3_session->status) { - pr_err("%s, display off!\n", __func__); + pr_err("display off!\n"); mutex_unlock(&mdp3_session->lock); - return -EPERM; + rc = -EPERM; + goto exit_err; } mdp3_clk_enable(1, 0); - rc = mdp3_session->dma->config_lut(mdp3_session->dma, &lut_config, - &lut); + rc = dma->config_lut(dma, &lut_config, cmap); mdp3_clk_enable(0, 0); if (rc) pr_err("mdp3_ctrl_lut_update failed\n"); @@ -1696,6 +1958,51 @@ static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd, mdp3_session->lut_sel = (mdp3_session->lut_sel + 1) % 2; mutex_unlock(&mdp3_session->lock); +exit_err: + mutex_unlock(&mdp3_session->pp_lock); + mdp3_free_lut_buffer(mfd->pdev, (void **) &cmap); + return rc; +} + +static int mdp3_ctrl_lut_read(struct msm_fb_data_type *mfd, + struct mdp_rgb_lut_data *cfg) +{ + int rc = 0; + struct fb_cmap *cmap; + struct mdp3_session_data *mdp3_session = mfd->mdp.private1; + struct mdp3_dma *dma = mdp3_session->dma; + + switch (cfg->lut_type) { + case mdp_rgb_lut_gc: + if (!dma->gc_cmap) { + pr_err("GC not programmed\n"); + return -EPERM; + } + cmap = dma->gc_cmap; + break; + case mdp_rgb_lut_hist: + if (!dma->hist_cmap) { + pr_err("Hist LUT not programmed\n"); + return -EPERM; + } + cmap = dma->hist_cmap; + break; + default: + pr_err("Invalid lut type %u\n", cfg->lut_type); + return -EINVAL; + } + + cfg->cmap.start = cmap->start; + cfg->cmap.len = cmap->len; + + mutex_lock(&mdp3_session->pp_lock); + rc = copy_to_user(cfg->cmap.red, cmap->red, sizeof(u16) * + MDP_LUT_SIZE); + rc |= copy_to_user(cfg->cmap.green, cmap->green, sizeof(u16) * + MDP_LUT_SIZE); + rc |= copy_to_user(cfg->cmap.blue, cmap->blue, sizeof(u16) * + MDP_LUT_SIZE); + mutex_unlock(&mdp3_session->pp_lock); return rc; } @@ -1929,7 +2236,7 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) mdp3_interface->dma_fnc = mdp3_ctrl_pan_display; mdp3_interface->ioctl_handler = mdp3_ctrl_ioctl_handler; mdp3_interface->kickoff_fnc = mdp3_ctrl_display_commit_kickoff; - mdp3_interface->lut_update = mdp3_ctrl_lut_update; + mdp3_interface->lut_update = NULL; mdp3_interface->configure_panel = mdp3_update_panel_info; mdp3_session = kzalloc(sizeof(struct mdp3_session_data), GFP_KERNEL); @@ -1942,6 +2249,7 @@ int mdp3_ctrl_init(struct msm_fb_data_type *mfd) INIT_WORK(&mdp3_session->dma_done_work, mdp3_dispatch_dma_done); atomic_set(&mdp3_session->vsync_countdown, 0); mutex_init(&mdp3_session->histo_lock); + mutex_init(&mdp3_session->pp_lock); mdp3_session->dma = mdp3_get_dma_pipe(MDP3_DMA_CAP_ALL); if (!mdp3_session->dma) { rc = -ENODEV; diff --git a/drivers/video/fbdev/msm/mdp3_ctrl.h b/drivers/video/fbdev/msm/mdp3_ctrl.h index 55e7e20bacdf..420907eb1525 100644 --- a/drivers/video/fbdev/msm/mdp3_ctrl.h +++ b/drivers/video/fbdev/msm/mdp3_ctrl.h @@ -25,6 +25,9 @@ #include "mdss_panel.h" #define MDP3_MAX_BUF_QUEUE 8 +#define MDP3_LUT_HIST_EN 0x001 +#define MDP3_LUT_GC_EN 0x002 +#define MDP3_LUT_HIST_GC_EN (MDP3_LUT_HIST_EN | MDP3_LUT_GC_EN) struct mdp3_buffer_queue { struct mdp3_img_data img_data[MDP3_MAX_BUF_QUEUE]; @@ -53,6 +56,7 @@ struct mdp3_session_data { atomic_t dma_done_cnt; int histo_status; struct mutex histo_lock; + struct mutex pp_lock; int lut_sel; int cc_vect_sel; bool vsync_before_commit; diff --git a/drivers/video/fbdev/msm/mdp3_dma.c b/drivers/video/fbdev/msm/mdp3_dma.c index 3a3a00a723cf..8bac9d84edde 100644 --- a/drivers/video/fbdev/msm/mdp3_dma.c +++ b/drivers/video/fbdev/msm/mdp3_dma.c @@ -552,20 +552,20 @@ static int mdp3_dmap_ccs_config(struct mdp3_dma *dma, static int mdp3_dmap_lut_config(struct mdp3_dma *dma, struct mdp3_dma_lut_config *config, - struct mdp3_dma_lut *lut) + struct fb_cmap *cmap) { u32 addr, color; int i; - if (config->lut_enable && lut) { + if (config->lut_enable && cmap) { addr = MDP3_REG_DMA_P_CSC_LUT1; if (config->lut_sel) addr = MDP3_REG_DMA_P_CSC_LUT2; for (i = 0; i < MDP_LUT_SIZE; i++) { - color = lut->color0_lut[i] & 0xff; - color |= (lut->color1_lut[i] & 0xff) << 8; - color |= (lut->color2_lut[i] & 0xff) << 16; + color = cmap->green[i] & 0xff; + color |= (cmap->red[i] & 0xff) << 8; + color |= (cmap->blue[i] & 0xff) << 16; MDP3_REG_WRITE(addr, color); addr += 4; } diff --git a/drivers/video/fbdev/msm/mdp3_dma.h b/drivers/video/fbdev/msm/mdp3_dma.h index 65a0b4e229fe..60c9e1dfb67f 100644 --- a/drivers/video/fbdev/msm/mdp3_dma.h +++ b/drivers/video/fbdev/msm/mdp3_dma.h @@ -191,12 +191,6 @@ struct mdp3_dma_ccs { u32 *post_lv; }; -struct mdp3_dma_lut { - u16 *color0_lut; - u16 *color1_lut; - u16 *color2_lut; -}; - struct mdp3_dma_lut_config { int lut_enable; u32 lut_sel; @@ -283,6 +277,10 @@ struct mdp3_dma { bool has_panic_ctrl; struct mdp3_rect roi; + u32 lut_sts; + struct fb_cmap *gc_cmap; + struct fb_cmap *hist_cmap; + int (*dma_config)(struct mdp3_dma *dma, struct mdp3_dma_source *source_config, struct mdp3_dma_output_config *output_config); @@ -305,7 +303,7 @@ struct mdp3_dma { int (*config_lut)(struct mdp3_dma *dma, struct mdp3_dma_lut_config *config, - struct mdp3_dma_lut *lut); + struct fb_cmap *cmap); int (*update)(struct mdp3_dma *dma, void *buf, struct mdp3_intf *intf, void *data); diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.c b/drivers/video/fbdev/msm/mdss_compat_utils.c index 7edf570b53a5..11888adb7f2b 100644 --- a/drivers/video/fbdev/msm/mdss_compat_utils.c +++ b/drivers/video/fbdev/msm/mdss_compat_utils.c @@ -422,6 +422,27 @@ static int __from_user_fb_cmap(struct fb_cmap __user *cmap, return 0; } +static int __to_user_fb_cmap(struct fb_cmap __user *cmap, + struct fb_cmap32 __user *cmap32) +{ + unsigned long data; + + if (copy_in_user(&cmap32->start, &cmap->start, 2 * sizeof(__u32))) + return -EFAULT; + + if (get_user(data, (unsigned long *) &cmap->red) || + put_user((compat_caddr_t) data, &cmap32->red) || + get_user(data, (unsigned long *) &cmap->green) || + put_user((compat_caddr_t) data, &cmap32->green) || + get_user(data, (unsigned long *) &cmap->blue) || + put_user((compat_caddr_t) data, &cmap32->blue) || + get_user(data, (unsigned long *) &cmap->transp) || + put_user((compat_caddr_t) data, &cmap32->transp)) + return -EFAULT; + + return 0; +} + static int __from_user_fb_image(struct fb_image __user *image, struct fb_image32 __user *image32) { @@ -1415,6 +1436,32 @@ static int __to_user_hist_lut_data( return 0; } +static int __from_user_rgb_lut_data( + struct mdp_rgb_lut_data32 __user *rgb_lut32, + struct mdp_rgb_lut_data __user *rgb_lut) +{ + if (copy_in_user(&rgb_lut->flags, &rgb_lut32->flags, + sizeof(uint32_t)) || + copy_in_user(&rgb_lut->lut_type, &rgb_lut32->lut_type, + sizeof(uint32_t))) + return -EFAULT; + + return __from_user_fb_cmap(&rgb_lut->cmap, &rgb_lut32->cmap); +} + +static int __to_user_rgb_lut_data( + struct mdp_rgb_lut_data32 __user *rgb_lut32, + struct mdp_rgb_lut_data __user *rgb_lut) +{ + if (copy_in_user(&rgb_lut32->flags, &rgb_lut->flags, + sizeof(uint32_t)) || + copy_in_user(&rgb_lut32->lut_type, &rgb_lut->lut_type, + sizeof(uint32_t))) + return -EFAULT; + + return __to_user_fb_cmap(&rgb_lut->cmap, &rgb_lut32->cmap); +} + static int __from_user_lut_cfg_data( struct mdp_lut_cfg_data32 __user *lut_cfg32, struct mdp_lut_cfg_data __user *lut_cfg) @@ -1447,6 +1494,11 @@ static int __from_user_lut_cfg_data( compat_ptr((uintptr_t)&lut_cfg32->data.hist_lut_data), &lut_cfg->data.hist_lut_data); break; + case mdp_lut_rgb: + ret = __from_user_rgb_lut_data( + compat_ptr((uintptr_t)&lut_cfg32->data.rgb_lut_data), + &lut_cfg->data.rgb_lut_data); + break; default: break; } @@ -1486,6 +1538,11 @@ static int __to_user_lut_cfg_data( compat_ptr((uintptr_t)&lut_cfg32->data.hist_lut_data), &lut_cfg->data.hist_lut_data); break; + case mdp_lut_rgb: + ret = __to_user_rgb_lut_data( + compat_ptr((uintptr_t)&lut_cfg32->data.rgb_lut_data), + &lut_cfg->data.rgb_lut_data); + break; default: break; } diff --git a/drivers/video/fbdev/msm/mdss_compat_utils.h b/drivers/video/fbdev/msm/mdss_compat_utils.h index a95e8223fd67..4b4fbc71f0b1 100644 --- a/drivers/video/fbdev/msm/mdss_compat_utils.h +++ b/drivers/video/fbdev/msm/mdss_compat_utils.h @@ -218,6 +218,12 @@ struct mdp_igc_lut_data_v1_7_32 { compat_caddr_t c2_data; }; +struct mdp_rgb_lut_data32 { + uint32_t flags; + uint32_t lut_type; + struct fb_cmap32 cmap; +}; + struct mdp_igc_lut_data32 { uint32_t block; uint32_t version; @@ -274,6 +280,7 @@ struct mdp_lut_cfg_data32 { struct mdp_igc_lut_data32 igc_lut_data; struct mdp_pgc_lut_data32 pgc_lut_data; struct mdp_hist_lut_data32 hist_lut_data; + struct mdp_rgb_lut_data32 rgb_lut_data; } data; }; diff --git a/include/uapi/linux/msm_mdp.h b/include/uapi/linux/msm_mdp.h index 8daedd3c1866..2a7ab5158f27 100644 --- a/include/uapi/linux/msm_mdp.h +++ b/include/uapi/linux/msm_mdp.h @@ -625,6 +625,7 @@ enum { mdp_lut_igc, mdp_lut_pgc, mdp_lut_hist, + mdp_lut_rgb, mdp_lut_max, }; struct mdp_overlay_pp_params { @@ -926,12 +927,28 @@ struct mdp_pgc_lut_data_v1_7 { uint32_t *c2_data; }; +/* + * mdp_rgb_lut_data is used to provide parameters for configuring the + * generic RGB lut in case of gamma correction or other LUT updation usecases + */ +struct mdp_rgb_lut_data { + uint32_t flags; + uint32_t lut_type; + struct fb_cmap cmap; +}; + +enum { + mdp_rgb_lut_gc, + mdp_rgb_lut_hist, +}; + struct mdp_lut_cfg_data { uint32_t lut_type; union { struct mdp_igc_lut_data igc_lut_data; struct mdp_pgc_lut_data pgc_lut_data; struct mdp_hist_lut_data hist_lut_data; + struct mdp_rgb_lut_data rgb_lut_data; } data; };