/* * Copyright (c) 2013-2015, The Linux Foundation. All rights reserved. * Copyright (C) 1994 Martin Schaller * * 2001 - Documented with DocBook * - Brad Douglas * * 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 #include #include #include "mdss_fb.h" #include "mdss_compat_utils.h" #include "mdss_mdp_hwio.h" #include "mdss_mdp.h" #define MSMFB_CURSOR32 _IOW(MSMFB_IOCTL_MAGIC, 130, struct fb_cursor32) #define MSMFB_SET_LUT32 _IOW(MSMFB_IOCTL_MAGIC, 131, struct fb_cmap32) #define MSMFB_HISTOGRAM32 _IOWR(MSMFB_IOCTL_MAGIC, 132,\ struct mdp_histogram_data32) #define MSMFB_GET_CCS_MATRIX32 _IOWR(MSMFB_IOCTL_MAGIC, 133, struct mdp_ccs32) #define MSMFB_SET_CCS_MATRIX32 _IOW(MSMFB_IOCTL_MAGIC, 134, struct mdp_ccs32) #define MSMFB_OVERLAY_SET32 _IOWR(MSMFB_IOCTL_MAGIC, 135,\ struct mdp_overlay32) #define MSMFB_OVERLAY_GET32 _IOR(MSMFB_IOCTL_MAGIC, 140,\ struct mdp_overlay32) #define MSMFB_OVERLAY_BLT32 _IOWR(MSMFB_IOCTL_MAGIC, 142,\ struct msmfb_overlay_blt32) #define MSMFB_HISTOGRAM_START32 _IOR(MSMFB_IOCTL_MAGIC, 144,\ struct mdp_histogram_start_req32) #define MSMFB_OVERLAY_3D32 _IOWR(MSMFB_IOCTL_MAGIC, 147,\ struct msmfb_overlay_3d32) #define MSMFB_MIXER_INFO32 _IOWR(MSMFB_IOCTL_MAGIC, 148,\ struct msmfb_mixer_info_req32) #define MSMFB_MDP_PP32 _IOWR(MSMFB_IOCTL_MAGIC, 156, struct msmfb_mdp_pp32) #define MSMFB_BUFFER_SYNC32 _IOW(MSMFB_IOCTL_MAGIC, 162, struct mdp_buf_sync32) #define MSMFB_OVERLAY_PREPARE32 _IOWR(MSMFB_IOCTL_MAGIC, 169, \ struct mdp_overlay_list32) #define MSMFB_ATOMIC_COMMIT32 _IOWR(MDP_IOCTL_MAGIC, 128, compat_caddr_t) #define MSMFB_ASYNC_POSITION_UPDATE_32 _IOWR(MDP_IOCTL_MAGIC, 129, \ struct mdp_position_update32) static int __copy_layer_pp_info_params(struct mdp_input_layer *layer, struct mdp_input_layer32 *layer32); static unsigned int __do_compat_ioctl_nr(unsigned int cmd32) { unsigned int cmd; switch (cmd32) { case MSMFB_CURSOR32: cmd = MSMFB_CURSOR; break; case MSMFB_SET_LUT32: cmd = MSMFB_SET_LUT; break; case MSMFB_HISTOGRAM32: cmd = MSMFB_HISTOGRAM; break; case MSMFB_GET_CCS_MATRIX32: cmd = MSMFB_GET_CCS_MATRIX; break; case MSMFB_SET_CCS_MATRIX32: cmd = MSMFB_SET_CCS_MATRIX; break; case MSMFB_OVERLAY_SET32: cmd = MSMFB_OVERLAY_SET; break; case MSMFB_OVERLAY_GET32: cmd = MSMFB_OVERLAY_GET; break; case MSMFB_OVERLAY_BLT32: cmd = MSMFB_OVERLAY_BLT; break; case MSMFB_OVERLAY_3D32: cmd = MSMFB_OVERLAY_3D; break; case MSMFB_MIXER_INFO32: cmd = MSMFB_MIXER_INFO; break; case MSMFB_MDP_PP32: cmd = MSMFB_MDP_PP; break; case MSMFB_BUFFER_SYNC32: cmd = MSMFB_BUFFER_SYNC; break; case MSMFB_OVERLAY_PREPARE32: cmd = MSMFB_OVERLAY_PREPARE; break; case MSMFB_ATOMIC_COMMIT32: cmd = MSMFB_ATOMIC_COMMIT; break; case MSMFB_ASYNC_POSITION_UPDATE_32: cmd = MSMFB_ASYNC_POSITION_UPDATE; break; default: cmd = cmd32; break; } return cmd; } static void __copy_atomic_commit_struct(struct mdp_layer_commit *commit, struct mdp_layer_commit32 *commit32) { commit->version = commit32->version; commit->commit_v1.flags = commit32->commit_v1.flags; commit->commit_v1.input_layer_cnt = commit32->commit_v1.input_layer_cnt; commit->commit_v1.left_roi = commit32->commit_v1.left_roi; commit->commit_v1.right_roi = commit32->commit_v1.right_roi; memcpy(&commit->commit_v1.reserved, &commit32->commit_v1.reserved, sizeof(commit32->commit_v1.reserved)); } static struct mdp_input_layer32 *__create_layer_list32( struct mdp_layer_commit32 *commit32, u32 layer_count) { u32 buffer_size32; struct mdp_input_layer32 *layer_list32; int ret; buffer_size32 = sizeof(struct mdp_input_layer32) * layer_count; layer_list32 = kmalloc(buffer_size32, GFP_KERNEL); if (!layer_list32) { pr_err("unable to allocate memory for layers32\n"); layer_list32 = ERR_PTR(-ENOMEM); goto end; } ret = copy_from_user(layer_list32, compat_ptr(commit32->commit_v1.input_layers), sizeof(struct mdp_input_layer32) * layer_count); if (ret) { pr_err("layer list32 copy from user failed, ptr %p\n", compat_ptr(commit32->commit_v1.input_layers)); kfree(layer_list32); ret = -EFAULT; layer_list32 = ERR_PTR(ret); } end: return layer_list32; } static int __copy_scale_params(struct mdp_input_layer *layer, struct mdp_input_layer32 *layer32) { struct mdp_scale_data *scale; int ret; if (!(layer->flags & MDP_LAYER_ENABLE_PIXEL_EXT)) return 0; scale = kmalloc(sizeof(struct mdp_scale_data), GFP_KERNEL); if (!scale) { pr_err("unable to allocate memory for scale param\n"); ret = -ENOMEM; goto end; } /* scale structure size is same for compat and 64bit version */ ret = copy_from_user(scale, compat_ptr(layer32->scale), sizeof(struct mdp_scale_data)); if (ret) { kfree(scale); pr_err("scale param copy from user failed, ptr %p\n", compat_ptr(layer32->scale)); ret = -EFAULT; } else { layer->scale = scale; } end: return ret; } static struct mdp_input_layer *__create_layer_list( struct mdp_layer_commit *commit, struct mdp_input_layer32 *layer_list32, u32 layer_count) { int i, ret; u32 buffer_size; struct mdp_input_layer *layer, *layer_list; struct mdp_input_layer32 *layer32; buffer_size = sizeof(struct mdp_input_layer) * layer_count; layer_list = kmalloc(buffer_size, GFP_KERNEL); if (!layer_list) { pr_err("unable to allocate memory for layers32\n"); layer_list = ERR_PTR(-ENOMEM); goto end; } commit->commit_v1.input_layers = layer_list; for (i = 0; i < layer_count; i++) { layer = &layer_list[i]; layer32 = &layer_list32[i]; layer->flags = layer32->flags; layer->pipe_ndx = layer32->pipe_ndx; layer->horz_deci = layer32->horz_deci; layer->vert_deci = layer32->vert_deci; layer->z_order = layer32->z_order; layer->transp_mask = layer32->transp_mask; layer->bg_color = layer32->bg_color; layer->blend_op = layer32->blend_op; layer->src_rect = layer32->src_rect; layer->dst_rect = layer32->dst_rect; layer->buffer = layer32->buffer; memcpy(&layer->reserved, &layer32->reserved, sizeof(layer->reserved)); layer->scale = NULL; ret = __copy_scale_params(layer, layer32); if (ret) break; layer->pp_info = NULL; ret = __copy_layer_pp_info_params(layer, layer32); if (ret) break; } if (ret) { for (i--; i >= 0; i--) { kfree(layer_list[i].scale); mdss_mdp_free_layer_pp_info(&layer_list[i]); } kfree(layer_list); layer_list = ERR_PTR(ret); } end: return layer_list; } static int __copy_to_user_atomic_commit(struct mdp_layer_commit *commit, struct mdp_layer_commit32 *commit32, struct mdp_input_layer32 *layer_list32, unsigned long argp, u32 layer_count) { int i, ret; struct mdp_input_layer *layer_list; layer_list = commit->commit_v1.input_layers; for (i = 0; i < layer_count; i++) layer_list32[i].error_code = layer_list[i].error_code; ret = copy_to_user(compat_ptr(commit32->commit_v1.input_layers), layer_list32, sizeof(struct mdp_input_layer32) * layer_count); if (ret) goto end; ret = copy_to_user(compat_ptr(commit32->commit_v1.output_layer), commit->commit_v1.output_layer, sizeof(struct mdp_output_layer)); if (ret) goto end; commit32->commit_v1.release_fence = commit->commit_v1.release_fence; commit32->commit_v1.retire_fence = commit->commit_v1.retire_fence; ret = copy_to_user((void __user *)argp, commit32, sizeof(struct mdp_layer_commit32)); end: return ret; } static int __compat_atomic_commit(struct fb_info *info, unsigned int cmd, unsigned long argp, struct file *file) { int ret, i; struct mdp_layer_commit commit; struct mdp_layer_commit32 commit32; u32 layer_count; struct mdp_input_layer *layer_list = NULL, *layer; struct mdp_input_layer32 *layer_list32 = NULL; struct mdp_output_layer *output_layer = NULL; /* copy top level memory from 32 bit structure to kernel memory */ ret = copy_from_user(&commit32, (void __user *)argp, sizeof(struct mdp_layer_commit32)); if (ret) { pr_err("%s:copy_from_user failed, ptr %p\n", __func__, (void __user *)argp); ret = -EFAULT; return ret; } __copy_atomic_commit_struct(&commit, &commit32); if (commit32.commit_v1.output_layer) { int buffer_size = sizeof(struct mdp_output_layer); output_layer = kzalloc(buffer_size, GFP_KERNEL); if (!output_layer) { pr_err("fail to allocate output layer\n"); return -ENOMEM; } ret = copy_from_user(output_layer, compat_ptr(commit32.commit_v1.output_layer), buffer_size); if (ret) { pr_err("fail to copy output layer from user, ptr %p\n", compat_ptr(commit32.commit_v1.output_layer)); ret = -EFAULT; goto layer_list_err; } commit.commit_v1.output_layer = output_layer; } layer_count = commit32.commit_v1.input_layer_cnt; if (layer_count > MAX_LAYER_COUNT) { ret = -EINVAL; goto layer_list_err; } else if (layer_count) { /* * allocate memory for layer list in 32bit domain and copy it * from user */ layer_list32 = __create_layer_list32(&commit32, layer_count); if (IS_ERR_OR_NULL(layer_list32)) { ret = PTR_ERR(layer_list32); goto layer_list_err; } /* * allocate memory for layer list in kernel memory domain and * copy layer info from 32bit structures to kernel memory */ layer_list = __create_layer_list(&commit, layer_list32, layer_count); if (IS_ERR_OR_NULL(layer_list)) { ret = PTR_ERR(layer_list); goto layer_list_err; } } ret = mdss_fb_atomic_commit(info, &commit, file); if (ret) pr_err("atomic commit failed ret:%d\n", ret); if (layer_count) __copy_to_user_atomic_commit(&commit, &commit32, layer_list32, argp, layer_count); for (i = 0; i < layer_count; i++) { kfree(layer[i].scale); mdss_mdp_free_layer_pp_info(&layer[i]); } kfree(layer_list); layer_list_err: kfree(layer_list32); kfree(output_layer); return ret; } static int __copy_to_user_async_position_update( struct mdp_position_update *update_pos, struct mdp_position_update32 *update_pos32, unsigned long argp, u32 layer_cnt) { int ret; ret = copy_to_user(update_pos32->input_layers, update_pos->input_layers, sizeof(struct mdp_async_layer) * layer_cnt); if (ret) goto end; ret = copy_to_user((void __user *) argp, update_pos32, sizeof(struct mdp_position_update32)); end: return ret; } static struct mdp_async_layer *__create_async_layer_list( struct mdp_position_update32 *update_pos32, u32 layer_cnt) { u32 buffer_size; struct mdp_async_layer *layer_list; int ret; buffer_size = sizeof(struct mdp_async_layer) * layer_cnt; layer_list = kmalloc(buffer_size, GFP_KERNEL); if (!layer_list) { layer_list = ERR_PTR(-ENOMEM); goto end; } ret = copy_from_user(layer_list, update_pos32->input_layers, buffer_size); if (ret) { pr_err("layer list32 copy from user failed\n"); kfree(layer_list); layer_list = ERR_PTR(ret); } end: return layer_list; } static int __compat_async_position_update(struct fb_info *info, unsigned int cmd, unsigned long argp) { struct mdp_position_update update_pos; struct mdp_position_update32 update_pos32; struct mdp_async_layer *layer_list = NULL; u32 layer_cnt, ret; /* copy top level memory from 32 bit structure to kernel memory */ ret = copy_from_user(&update_pos32, (void __user *)argp, sizeof(struct mdp_position_update32)); if (ret) { pr_err("%s:copy_from_user failed\n", __func__); return ret; } update_pos.input_layer_cnt = update_pos32.input_layer_cnt; layer_cnt = update_pos32.input_layer_cnt; if (!layer_cnt) { pr_err("no async layer to update\n"); return -EINVAL; } layer_list = __create_async_layer_list(&update_pos32, layer_cnt); if (IS_ERR_OR_NULL(layer_list)) return PTR_ERR(layer_list); update_pos.input_layers = layer_list; ret = mdss_fb_async_position_update(info, &update_pos); if (ret) pr_err("async position update failed ret:%d\n", ret); ret = __copy_to_user_async_position_update(&update_pos, &update_pos32, argp, layer_cnt); if (ret) pr_err("copy to user of async update position failed\n"); kfree(layer_list); return ret; } static int mdss_fb_compat_buf_sync(struct fb_info *info, unsigned int cmd, unsigned long arg, struct file *file) { struct mdp_buf_sync32 __user *buf_sync32; struct mdp_buf_sync __user *buf_sync; u32 data; int ret; buf_sync = compat_alloc_user_space(sizeof(*buf_sync)); if (!buf_sync) { pr_err("%s:%u: compat alloc error [%zu] bytes\n", __func__, __LINE__, sizeof(*buf_sync)); return -EINVAL; } buf_sync32 = compat_ptr(arg); if (copy_in_user(&buf_sync->flags, &buf_sync32->flags, 3 * sizeof(u32))) return -EFAULT; if (get_user(data, &buf_sync32->acq_fen_fd) || put_user(compat_ptr(data), &buf_sync->acq_fen_fd) || get_user(data, &buf_sync32->rel_fen_fd) || put_user(compat_ptr(data), &buf_sync->rel_fen_fd) || get_user(data, &buf_sync32->retire_fen_fd) || put_user(compat_ptr(data), &buf_sync->retire_fen_fd)) return -EFAULT; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) buf_sync, file); if (ret) { pr_err("%s: failed %d\n", __func__, ret); return ret; } if (copy_in_user(compat_ptr(buf_sync32->rel_fen_fd), buf_sync->rel_fen_fd, sizeof(int))) return -EFAULT; if (copy_in_user(compat_ptr(buf_sync32->retire_fen_fd), buf_sync->retire_fen_fd, sizeof(int))) { if (buf_sync->flags & MDP_BUF_SYNC_FLAG_RETIRE_FENCE) return -EFAULT; else pr_debug("%s: no retire fence fd for wb\n", __func__); } return ret; } static int __from_user_fb_cmap(struct fb_cmap __user *cmap, struct fb_cmap32 __user *cmap32) { __u32 data; if (copy_in_user(&cmap->start, &cmap32->start, 2 * sizeof(__u32))) return -EFAULT; if (get_user(data, &cmap32->red) || put_user(compat_ptr(data), &cmap->red) || get_user(data, &cmap32->green) || put_user(compat_ptr(data), &cmap->green) || get_user(data, &cmap32->blue) || put_user(compat_ptr(data), &cmap->blue) || get_user(data, &cmap32->transp) || put_user(compat_ptr(data), &cmap->transp)) return -EFAULT; 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) { __u32 data; if (copy_in_user(&image->dx, &image32->dx, 6 * sizeof(u32)) || copy_in_user(&image->depth, &image32->depth, sizeof(u8))) return -EFAULT; if (get_user(data, &image32->data) || put_user(compat_ptr(data), &image->data)) return -EFAULT; if (__from_user_fb_cmap(&image->cmap, &image32->cmap)) return -EFAULT; return 0; } static int mdss_fb_compat_cursor(struct fb_info *info, unsigned int cmd, unsigned long arg, struct file *file) { struct fb_cursor32 __user *cursor32; struct fb_cursor __user *cursor; __u32 data; int ret; cursor = compat_alloc_user_space(sizeof(*cursor)); if (!cursor) { pr_err("%s:%u: compat alloc error [%zu] bytes\n", __func__, __LINE__, sizeof(*cursor)); return -EINVAL; } cursor32 = compat_ptr(arg); if (copy_in_user(&cursor->set, &cursor32->set, 3 * sizeof(u16))) return -EFAULT; if (get_user(data, &cursor32->mask) || put_user(compat_ptr(data), &cursor->mask)) return -EFAULT; if (copy_in_user(&cursor->hot, &cursor32->hot, sizeof(struct fbcurpos))) return -EFAULT; if (__from_user_fb_image(&cursor->image, &cursor32->image)) return -EFAULT; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) cursor, file); return ret; } static int mdss_fb_compat_set_lut(struct fb_info *info, unsigned long arg, struct file *file) { struct fb_cmap_user __user *cmap; struct fb_cmap32 __user *cmap32; __u32 data; int ret; cmap = compat_alloc_user_space(sizeof(*cmap)); cmap32 = compat_ptr(arg); if (copy_in_user(&cmap->start, &cmap32->start, 2 * sizeof(__u32))) return -EFAULT; if (get_user(data, &cmap32->red) || put_user(compat_ptr(data), &cmap->red) || get_user(data, &cmap32->green) || put_user(compat_ptr(data), &cmap->green) || get_user(data, &cmap32->blue) || put_user(compat_ptr(data), &cmap->blue) || get_user(data, &cmap32->transp) || put_user(compat_ptr(data), &cmap->transp)) return -EFAULT; ret = mdss_fb_do_ioctl(info, MSMFB_SET_LUT, (unsigned long) cmap, file); if (!ret) pr_debug("%s: compat ioctl successful\n", __func__); return ret; } static int __from_user_sharp_cfg( struct mdp_sharp_cfg32 __user *sharp_cfg32, struct mdp_sharp_cfg __user *sharp_cfg) { if (copy_in_user(&sharp_cfg->flags, &sharp_cfg32->flags, sizeof(uint32_t)) || copy_in_user(&sharp_cfg->strength, &sharp_cfg32->strength, sizeof(uint32_t)) || copy_in_user(&sharp_cfg->edge_thr, &sharp_cfg32->edge_thr, sizeof(uint32_t)) || copy_in_user(&sharp_cfg->smooth_thr, &sharp_cfg32->smooth_thr, sizeof(uint32_t)) || copy_in_user(&sharp_cfg->noise_thr, &sharp_cfg32->noise_thr, sizeof(uint32_t))) return -EFAULT; return 0; } static int __to_user_sharp_cfg( struct mdp_sharp_cfg32 __user *sharp_cfg32, struct mdp_sharp_cfg __user *sharp_cfg) { if (copy_in_user(&sharp_cfg32->flags, &sharp_cfg->flags, sizeof(uint32_t)) || copy_in_user(&sharp_cfg32->strength, &sharp_cfg->strength, sizeof(uint32_t)) || copy_in_user(&sharp_cfg32->edge_thr, &sharp_cfg->edge_thr, sizeof(uint32_t)) || copy_in_user(&sharp_cfg32->smooth_thr, &sharp_cfg->smooth_thr, sizeof(uint32_t)) || copy_in_user(&sharp_cfg32->noise_thr, &sharp_cfg->noise_thr, sizeof(uint32_t))) return -EFAULT; return 0; } static int __from_user_histogram_cfg( struct mdp_histogram_cfg32 __user *hist_cfg32, struct mdp_histogram_cfg __user *hist_cfg) { if (copy_in_user(&hist_cfg->ops, &hist_cfg32->ops, sizeof(uint32_t)) || copy_in_user(&hist_cfg->block, &hist_cfg32->block, sizeof(uint32_t)) || copy_in_user(&hist_cfg->frame_cnt, &hist_cfg32->frame_cnt, sizeof(uint8_t)) || copy_in_user(&hist_cfg->bit_mask, &hist_cfg32->bit_mask, sizeof(uint8_t)) || copy_in_user(&hist_cfg->num_bins, &hist_cfg32->num_bins, sizeof(uint16_t))) return -EFAULT; return 0; } static int __to_user_histogram_cfg( struct mdp_histogram_cfg32 __user *hist_cfg32, struct mdp_histogram_cfg __user *hist_cfg) { if (copy_in_user(&hist_cfg32->ops, &hist_cfg->ops, sizeof(uint32_t)) || copy_in_user(&hist_cfg32->block, &hist_cfg->block, sizeof(uint32_t)) || copy_in_user(&hist_cfg32->frame_cnt, &hist_cfg->frame_cnt, sizeof(uint8_t)) || copy_in_user(&hist_cfg32->bit_mask, &hist_cfg->bit_mask, sizeof(uint8_t)) || copy_in_user(&hist_cfg32->num_bins, &hist_cfg->num_bins, sizeof(uint16_t))) return -EFAULT; return 0; } static int __from_user_pcc_coeff( struct mdp_pcc_coeff32 __user *pcc_coeff32, struct mdp_pcc_coeff __user *pcc_coeff) { if (copy_in_user(&pcc_coeff->c, &pcc_coeff32->c, sizeof(uint32_t)) || copy_in_user(&pcc_coeff->r, &pcc_coeff32->r, sizeof(uint32_t)) || copy_in_user(&pcc_coeff->g, &pcc_coeff32->g, sizeof(uint32_t)) || copy_in_user(&pcc_coeff->b, &pcc_coeff32->b, sizeof(uint32_t)) || copy_in_user(&pcc_coeff->rr, &pcc_coeff32->rr, sizeof(uint32_t)) || copy_in_user(&pcc_coeff->gg, &pcc_coeff32->gg, sizeof(uint32_t)) || copy_in_user(&pcc_coeff->bb, &pcc_coeff32->bb, sizeof(uint32_t)) || copy_in_user(&pcc_coeff->rg, &pcc_coeff32->rg, sizeof(uint32_t)) || copy_in_user(&pcc_coeff->gb, &pcc_coeff32->gb, sizeof(uint32_t)) || copy_in_user(&pcc_coeff->rb, &pcc_coeff32->rb, sizeof(uint32_t)) || copy_in_user(&pcc_coeff->rgb_0, &pcc_coeff32->rgb_0, sizeof(uint32_t)) || copy_in_user(&pcc_coeff->rgb_1, &pcc_coeff32->rgb_1, sizeof(uint32_t))) return -EFAULT; return 0; } static int __to_user_pcc_coeff( struct mdp_pcc_coeff32 __user *pcc_coeff32, struct mdp_pcc_coeff __user *pcc_coeff) { if (copy_in_user(&pcc_coeff32->c, &pcc_coeff->c, sizeof(uint32_t)) || copy_in_user(&pcc_coeff32->r, &pcc_coeff->r, sizeof(uint32_t)) || copy_in_user(&pcc_coeff32->g, &pcc_coeff->g, sizeof(uint32_t)) || copy_in_user(&pcc_coeff32->b, &pcc_coeff->b, sizeof(uint32_t)) || copy_in_user(&pcc_coeff32->rr, &pcc_coeff->rr, sizeof(uint32_t)) || copy_in_user(&pcc_coeff32->gg, &pcc_coeff->gg, sizeof(uint32_t)) || copy_in_user(&pcc_coeff32->bb, &pcc_coeff->bb, sizeof(uint32_t)) || copy_in_user(&pcc_coeff32->rg, &pcc_coeff->rg, sizeof(uint32_t)) || copy_in_user(&pcc_coeff32->gb, &pcc_coeff->gb, sizeof(uint32_t)) || copy_in_user(&pcc_coeff32->rb, &pcc_coeff->rb, sizeof(uint32_t)) || copy_in_user(&pcc_coeff32->rgb_0, &pcc_coeff->rgb_0, sizeof(uint32_t)) || copy_in_user(&pcc_coeff32->rgb_1, &pcc_coeff->rgb_1, sizeof(uint32_t))) return -EFAULT; return 0; } static int __from_user_pcc_coeff_v17( struct mdp_pcc_cfg_data32 __user *pcc_cfg32, struct mdp_pcc_cfg_data __user *pcc_cfg) { struct mdp_pcc_data_v1_7_32 pcc_cfg_payload32; struct mdp_pcc_data_v1_7 pcc_cfg_payload; if (copy_from_user(&pcc_cfg_payload32, compat_ptr(pcc_cfg32->cfg_payload), sizeof(struct mdp_pcc_data_v1_7_32))) { pr_err("failed to copy payload for pcc from user\n"); return -EFAULT; } pcc_cfg_payload.r.b = pcc_cfg_payload32.r.b; pcc_cfg_payload.r.g = pcc_cfg_payload32.r.g; pcc_cfg_payload.r.c = pcc_cfg_payload32.r.c; pcc_cfg_payload.r.r = pcc_cfg_payload32.r.r; pcc_cfg_payload.r.gb = pcc_cfg_payload32.r.gb; pcc_cfg_payload.r.rb = pcc_cfg_payload32.r.rb; pcc_cfg_payload.r.rg = pcc_cfg_payload32.r.rg; pcc_cfg_payload.r.rgb = pcc_cfg_payload32.r.rgb; pcc_cfg_payload.g.b = pcc_cfg_payload32.g.b; pcc_cfg_payload.g.g = pcc_cfg_payload32.g.g; pcc_cfg_payload.g.c = pcc_cfg_payload32.g.c; pcc_cfg_payload.g.r = pcc_cfg_payload32.g.r; pcc_cfg_payload.g.gb = pcc_cfg_payload32.g.gb; pcc_cfg_payload.g.rb = pcc_cfg_payload32.g.rb; pcc_cfg_payload.g.rg = pcc_cfg_payload32.g.rg; pcc_cfg_payload.g.rgb = pcc_cfg_payload32.g.rgb; pcc_cfg_payload.b.b = pcc_cfg_payload32.b.b; pcc_cfg_payload.b.g = pcc_cfg_payload32.b.g; pcc_cfg_payload.b.c = pcc_cfg_payload32.b.c; pcc_cfg_payload.b.r = pcc_cfg_payload32.b.r; pcc_cfg_payload.b.gb = pcc_cfg_payload32.b.gb; pcc_cfg_payload.b.rb = pcc_cfg_payload32.b.rb; pcc_cfg_payload.b.rg = pcc_cfg_payload32.b.rg; pcc_cfg_payload.b.rgb = pcc_cfg_payload32.b.rgb; if (copy_to_user(pcc_cfg->cfg_payload, &pcc_cfg_payload, sizeof(pcc_cfg_payload))) { pr_err("failed to copy payload for pcc to user\n"); return -EFAULT; } return 0; } static int __from_user_pcc_cfg_data( struct mdp_pcc_cfg_data32 __user *pcc_cfg32, struct mdp_pcc_cfg_data __user *pcc_cfg) { u32 version; if (copy_in_user(&pcc_cfg->block, &pcc_cfg32->block, sizeof(uint32_t)) || copy_in_user(&pcc_cfg->ops, &pcc_cfg32->ops, sizeof(uint32_t)) || copy_in_user(&pcc_cfg->version, &pcc_cfg32->version, sizeof(uint32_t))) return -EFAULT; if (copy_from_user(&version, &pcc_cfg32->version, sizeof(u32))) { pr_err("failed to copy version for pcc\n"); return -EFAULT; } switch (version) { case mdp_pcc_v1_7: if (__from_user_pcc_coeff_v17(pcc_cfg32, pcc_cfg)) { pr_err("failed to copy pcc v17 data\n"); return -EFAULT; } break; default: pr_debug("pcc version %d not supported use legacy\n", version); if (__from_user_pcc_coeff( compat_ptr((uintptr_t)&pcc_cfg32->r), &pcc_cfg->r) || __from_user_pcc_coeff( compat_ptr((uintptr_t)&pcc_cfg32->g), &pcc_cfg->g) || __from_user_pcc_coeff( compat_ptr((uintptr_t)&pcc_cfg32->b), &pcc_cfg->b)) return -EFAULT; break; } return 0; } static int __to_user_pcc_coeff_v1_7( struct mdp_pcc_cfg_data32 __user *pcc_cfg32, struct mdp_pcc_cfg_data __user *pcc_cfg) { struct mdp_pcc_data_v1_7_32 pcc_cfg_payload32; struct mdp_pcc_data_v1_7 pcc_cfg_payload; if (copy_from_user(&pcc_cfg_payload, pcc_cfg->cfg_payload, sizeof(struct mdp_pcc_data_v1_7))) { pr_err("failed to copy payload for pcc from user\n"); return -EFAULT; } pcc_cfg_payload32.r.b = pcc_cfg_payload.r.b; pcc_cfg_payload32.r.g = pcc_cfg_payload.r.g; pcc_cfg_payload32.r.c = pcc_cfg_payload.r.c; pcc_cfg_payload32.r.r = pcc_cfg_payload.r.r; pcc_cfg_payload32.r.gb = pcc_cfg_payload.r.gb; pcc_cfg_payload32.r.rb = pcc_cfg_payload.r.rb; pcc_cfg_payload32.r.rg = pcc_cfg_payload.r.rg; pcc_cfg_payload32.r.rgb = pcc_cfg_payload.r.rgb; pcc_cfg_payload32.g.b = pcc_cfg_payload.g.b; pcc_cfg_payload32.g.g = pcc_cfg_payload.g.g; pcc_cfg_payload32.g.c = pcc_cfg_payload.g.c; pcc_cfg_payload32.g.r = pcc_cfg_payload.g.r; pcc_cfg_payload32.g.gb = pcc_cfg_payload.g.gb; pcc_cfg_payload32.g.rb = pcc_cfg_payload.g.rb; pcc_cfg_payload32.g.rg = pcc_cfg_payload.g.rg; pcc_cfg_payload32.g.rgb = pcc_cfg_payload.g.rgb; pcc_cfg_payload32.b.b = pcc_cfg_payload.b.b; pcc_cfg_payload32.b.g = pcc_cfg_payload.b.g; pcc_cfg_payload32.b.c = pcc_cfg_payload.b.c; pcc_cfg_payload32.b.r = pcc_cfg_payload.b.r; pcc_cfg_payload32.b.gb = pcc_cfg_payload.b.gb; pcc_cfg_payload32.b.rb = pcc_cfg_payload.b.rb; pcc_cfg_payload32.b.rg = pcc_cfg_payload.b.rg; pcc_cfg_payload32.b.rgb = pcc_cfg_payload.b.rgb; if (copy_to_user(compat_ptr(pcc_cfg32->cfg_payload), &pcc_cfg_payload32, sizeof(pcc_cfg_payload32))) { pr_err("failed to copy payload for pcc to user\n"); return -EFAULT; } return 0; } static int __to_user_pcc_cfg_data( struct mdp_pcc_cfg_data32 __user *pcc_cfg32, struct mdp_pcc_cfg_data __user *pcc_cfg) { u32 version; u32 ops; if (copy_from_user(&ops, &pcc_cfg->ops, sizeof(u32))) { pr_err("failed to copy op for pcc\n"); return -EFAULT; } if (!(ops & MDP_PP_OPS_READ)) { pr_debug("Read op is not set. Skipping compat copyback\n"); return 0; } if (copy_from_user(&version, &pcc_cfg->version, sizeof(u32))) { pr_err("failed to copy version for pcc\n"); return -EFAULT; } switch (version) { case mdp_pcc_v1_7: if (__to_user_pcc_coeff_v1_7(pcc_cfg32, pcc_cfg)) { pr_err("failed to copy pcc v1_7 data\n"); return -EFAULT; } break; default: pr_debug("version invalid, fallback to legacy\n"); if (__to_user_pcc_coeff( compat_ptr((uintptr_t)&pcc_cfg32->r), &pcc_cfg->r) || __to_user_pcc_coeff( compat_ptr((uintptr_t)&pcc_cfg32->g), &pcc_cfg->g) || __to_user_pcc_coeff( compat_ptr((uintptr_t)&pcc_cfg32->b), &pcc_cfg->b)) return -EFAULT; break; } return 0; } static int __from_user_csc_cfg( struct mdp_csc_cfg32 __user *csc_data32, struct mdp_csc_cfg __user *csc_data) { if (copy_in_user(&csc_data->flags, &csc_data32->flags, sizeof(uint32_t)) || copy_in_user(&csc_data->csc_mv[0], &csc_data32->csc_mv[0], 9 * sizeof(uint32_t)) || copy_in_user(&csc_data->csc_pre_bv[0], &csc_data32->csc_pre_bv[0], 3 * sizeof(uint32_t)) || copy_in_user(&csc_data->csc_post_bv[0], &csc_data32->csc_post_bv[0], 3 * sizeof(uint32_t)) || copy_in_user(&csc_data->csc_pre_lv[0], &csc_data32->csc_pre_lv[0], 6 * sizeof(uint32_t)) || copy_in_user(&csc_data->csc_post_lv[0], &csc_data32->csc_post_lv[0], 6 * sizeof(uint32_t))) return -EFAULT; return 0; } static int __to_user_csc_cfg( struct mdp_csc_cfg32 __user *csc_data32, struct mdp_csc_cfg __user *csc_data) { if (copy_in_user(&csc_data32->flags, &csc_data->flags, sizeof(uint32_t)) || copy_in_user(&csc_data32->csc_mv[0], &csc_data->csc_mv[0], 9 * sizeof(uint32_t)) || copy_in_user(&csc_data32->csc_pre_bv[0], &csc_data->csc_pre_bv[0], 3 * sizeof(uint32_t)) || copy_in_user(&csc_data32->csc_post_bv[0], &csc_data->csc_post_bv[0], 3 * sizeof(uint32_t)) || copy_in_user(&csc_data32->csc_pre_lv[0], &csc_data->csc_pre_lv[0], 6 * sizeof(uint32_t)) || copy_in_user(&csc_data32->csc_post_lv[0], &csc_data->csc_post_lv[0], 6 * sizeof(uint32_t))) return -EFAULT; return 0; } static int __from_user_csc_cfg_data( struct mdp_csc_cfg_data32 __user *csc_cfg32, struct mdp_csc_cfg_data __user *csc_cfg) { if (copy_in_user(&csc_cfg->block, &csc_cfg32->block, sizeof(uint32_t))) return -EFAULT; if (__from_user_csc_cfg( compat_ptr((uintptr_t)&csc_cfg32->csc_data), &csc_cfg->csc_data)) return -EFAULT; return 0; } static int __to_user_csc_cfg_data( struct mdp_csc_cfg_data32 __user *csc_cfg32, struct mdp_csc_cfg_data __user *csc_cfg) { if (copy_in_user(&csc_cfg32->block, &csc_cfg->block, sizeof(uint32_t))) return -EFAULT; if (__to_user_csc_cfg( compat_ptr((uintptr_t)&csc_cfg32->csc_data), &csc_cfg->csc_data)) return -EFAULT; return 0; } static int __from_user_igc_lut_data_v17( struct mdp_igc_lut_data32 __user *igc_lut32, struct mdp_igc_lut_data __user *igc_lut) { struct mdp_igc_lut_data_v1_7_32 igc_cfg_payload_32; struct mdp_igc_lut_data_v1_7 igc_cfg_payload; if (copy_from_user(&igc_cfg_payload_32, compat_ptr(igc_lut32->cfg_payload), sizeof(igc_cfg_payload_32))) { pr_err("failed to copy payload from user for igc\n"); return -EFAULT; } igc_cfg_payload.c0_c1_data = compat_ptr(igc_cfg_payload_32.c0_c1_data); igc_cfg_payload.c2_data = compat_ptr(igc_cfg_payload_32.c2_data); igc_cfg_payload.len = igc_cfg_payload_32.len; igc_cfg_payload.table_fmt = igc_cfg_payload_32.table_fmt; if (copy_to_user(igc_lut->cfg_payload, &igc_cfg_payload, sizeof(igc_cfg_payload))) { pr_err("failed to copy payload to user for igc\n"); return -EFAULT; } return 0; } static int __from_user_igc_lut_data( struct mdp_igc_lut_data32 __user *igc_lut32, struct mdp_igc_lut_data __user *igc_lut) { uint32_t data; uint32_t version = mdp_igc_vmax; int ret = 0; if (copy_in_user(&igc_lut->block, &igc_lut32->block, sizeof(uint32_t)) || copy_in_user(&igc_lut->len, &igc_lut32->len, sizeof(uint32_t)) || copy_in_user(&igc_lut->ops, &igc_lut32->ops, sizeof(uint32_t)) || copy_in_user(&igc_lut->version, &igc_lut32->version, sizeof(uint32_t))) return -EFAULT; if (get_user(version, &igc_lut32->version)) { pr_err("failed to copy the version for IGC\n"); return -EFAULT; } switch (version) { case mdp_igc_v1_7: ret = __from_user_igc_lut_data_v17(igc_lut32, igc_lut); if (ret) pr_err("failed to copy payload for igc version %d ret %d\n", version, ret); break; default: pr_debug("version not supported fallback to legacy %d\n", version); if (get_user(data, &igc_lut32->c0_c1_data) || put_user(compat_ptr(data), &igc_lut->c0_c1_data) || get_user(data, &igc_lut32->c2_data) || put_user(compat_ptr(data), &igc_lut->c2_data)) return -EFAULT; break; } return ret; } static int __to_user_igc_lut_data( struct mdp_igc_lut_data32 __user *igc_lut32, struct mdp_igc_lut_data __user *igc_lut) { unsigned long data; if (copy_in_user(&igc_lut32->block, &igc_lut->block, sizeof(uint32_t)) || copy_in_user(&igc_lut32->len, &igc_lut->len, sizeof(uint32_t)) || copy_in_user(&igc_lut32->ops, &igc_lut->ops, sizeof(uint32_t))) return -EFAULT; if (get_user(data, (unsigned long *) &igc_lut->c0_c1_data) || put_user((compat_caddr_t) data, &igc_lut32->c0_c1_data) || get_user(data, (unsigned long *) &igc_lut->c2_data) || put_user((compat_caddr_t) data, &igc_lut32->c2_data)) return -EFAULT; return 0; } static int __from_user_ar_gc_lut_data( struct mdp_ar_gc_lut_data32 __user *ar_gc_data32, struct mdp_ar_gc_lut_data __user *ar_gc_data) { if (copy_in_user(&ar_gc_data->x_start, &ar_gc_data32->x_start, sizeof(uint32_t)) || copy_in_user(&ar_gc_data->slope, &ar_gc_data32->slope, sizeof(uint32_t)) || copy_in_user(&ar_gc_data->offset, &ar_gc_data32->offset, sizeof(uint32_t))) return -EFAULT; return 0; } static int __to_user_ar_gc_lut_data( struct mdp_ar_gc_lut_data32 __user *ar_gc_data32, struct mdp_ar_gc_lut_data __user *ar_gc_data) { if (copy_in_user(&ar_gc_data32->x_start, &ar_gc_data->x_start, sizeof(uint32_t)) || copy_in_user(&ar_gc_data32->slope, &ar_gc_data->slope, sizeof(uint32_t)) || copy_in_user(&ar_gc_data32->offset, &ar_gc_data->offset, sizeof(uint32_t))) return -EFAULT; return 0; } static int __from_user_pgc_lut_data_v1_7( struct mdp_pgc_lut_data32 __user *pgc_lut32, struct mdp_pgc_lut_data __user *pgc_lut) { struct mdp_pgc_lut_data_v1_7_32 pgc_cfg_payload_32; struct mdp_pgc_lut_data_v1_7 pgc_cfg_payload; if (copy_from_user(&pgc_cfg_payload_32, compat_ptr(pgc_lut32->cfg_payload), sizeof(pgc_cfg_payload_32))) { pr_err("failed to copy from user the pgc32 payload\n"); return -EFAULT; } pgc_cfg_payload.c0_data = compat_ptr(pgc_cfg_payload_32.c0_data); pgc_cfg_payload.c1_data = compat_ptr(pgc_cfg_payload_32.c1_data); pgc_cfg_payload.c2_data = compat_ptr(pgc_cfg_payload_32.c2_data); pgc_cfg_payload.len = pgc_cfg_payload_32.len; if (copy_to_user(pgc_lut->cfg_payload, &pgc_cfg_payload, sizeof(pgc_cfg_payload))) { pr_err("failed to copy to user pgc payload\n"); return -EFAULT; } return 0; } static int __from_user_pgc_lut_data_legacy( struct mdp_pgc_lut_data32 __user *pgc_lut32, struct mdp_pgc_lut_data __user *pgc_lut) { struct mdp_ar_gc_lut_data32 __user *r_data_temp32; struct mdp_ar_gc_lut_data32 __user *g_data_temp32; struct mdp_ar_gc_lut_data32 __user *b_data_temp32; struct mdp_ar_gc_lut_data __user *r_data_temp; struct mdp_ar_gc_lut_data __user *g_data_temp; struct mdp_ar_gc_lut_data __user *b_data_temp; uint8_t num_r_stages, num_g_stages, num_b_stages; int i; if (copy_from_user(&num_r_stages, &pgc_lut32->num_r_stages, sizeof(uint8_t)) || copy_from_user(&num_g_stages, &pgc_lut32->num_g_stages, sizeof(uint8_t)) || copy_from_user(&num_b_stages, &pgc_lut32->num_b_stages, sizeof(uint8_t))) return -EFAULT; if (num_r_stages > GC_LUT_SEGMENTS || num_b_stages > GC_LUT_SEGMENTS || num_r_stages > GC_LUT_SEGMENTS || !num_r_stages || !num_b_stages || !num_g_stages) { pr_err("invalid number of stages r_stages %d b_stages %d g_stages %d\n", num_r_stages, num_b_stages, num_r_stages); return -EFAULT; } r_data_temp32 = compat_ptr((uintptr_t)pgc_lut32->r_data); r_data_temp = pgc_lut->r_data; for (i = 0; i < num_r_stages; i++) { if (__from_user_ar_gc_lut_data( &r_data_temp32[i], &r_data_temp[i])) return -EFAULT; } g_data_temp32 = compat_ptr((uintptr_t)pgc_lut32->g_data); g_data_temp = pgc_lut->g_data; for (i = 0; i < num_g_stages; i++) { if (__from_user_ar_gc_lut_data( &g_data_temp32[i], &g_data_temp[i])) return -EFAULT; } b_data_temp32 = compat_ptr((uintptr_t)pgc_lut32->b_data); b_data_temp = pgc_lut->b_data; for (i = 0; i < num_b_stages; i++) { if (__from_user_ar_gc_lut_data( &b_data_temp32[i], &b_data_temp[i])) return -EFAULT; } return 0; } static int __from_user_pgc_lut_data( struct mdp_pgc_lut_data32 __user *pgc_lut32, struct mdp_pgc_lut_data __user *pgc_lut) { u32 version = mdp_pgc_vmax; int ret = 0; if (copy_in_user(&pgc_lut->block, &pgc_lut32->block, sizeof(uint32_t)) || copy_in_user(&pgc_lut->flags, &pgc_lut32->flags, sizeof(uint32_t)) || copy_in_user(&pgc_lut->num_r_stages, &pgc_lut32->num_r_stages, sizeof(uint8_t)) || copy_in_user(&pgc_lut->num_g_stages, &pgc_lut32->num_g_stages, sizeof(uint8_t)) || copy_in_user(&pgc_lut->num_b_stages, &pgc_lut32->num_b_stages, sizeof(uint8_t)) || copy_in_user(&pgc_lut->version, &pgc_lut32->version, sizeof(uint32_t))) return -EFAULT; if (copy_from_user(&version, &pgc_lut32->version, sizeof(u32))) { pr_err("version copying failed\n"); return -EFAULT; } switch (version) { case mdp_pgc_v1_7: ret = __from_user_pgc_lut_data_v1_7(pgc_lut32, pgc_lut); if (ret) pr_err("failed to copy pgc v17\n"); break; default: pr_debug("version %d not supported fallback to legacy\n", version); ret = __from_user_pgc_lut_data_legacy(pgc_lut32, pgc_lut); if (ret) pr_err("copy from user pgc lut legacy failed ret %d\n", ret); break; } return ret; } static int __to_user_pgc_lut_data( struct mdp_pgc_lut_data32 __user *pgc_lut32, struct mdp_pgc_lut_data __user *pgc_lut) { struct mdp_ar_gc_lut_data32 __user *r_data_temp32; struct mdp_ar_gc_lut_data32 __user *g_data_temp32; struct mdp_ar_gc_lut_data32 __user *b_data_temp32; struct mdp_ar_gc_lut_data __user *r_data_temp; struct mdp_ar_gc_lut_data __user *g_data_temp; struct mdp_ar_gc_lut_data __user *b_data_temp; uint8_t num_r_stages, num_g_stages, num_b_stages; int i; if (copy_in_user(&pgc_lut32->block, &pgc_lut->block, sizeof(uint32_t)) || copy_in_user(&pgc_lut32->flags, &pgc_lut->flags, sizeof(uint32_t)) || copy_in_user(&pgc_lut32->num_r_stages, &pgc_lut->num_r_stages, sizeof(uint8_t)) || copy_in_user(&pgc_lut32->num_g_stages, &pgc_lut->num_g_stages, sizeof(uint8_t)) || copy_in_user(&pgc_lut32->num_b_stages, &pgc_lut->num_b_stages, sizeof(uint8_t))) return -EFAULT; if (copy_from_user(&num_r_stages, &pgc_lut->num_r_stages, sizeof(uint8_t)) || copy_from_user(&num_g_stages, &pgc_lut->num_g_stages, sizeof(uint8_t)) || copy_from_user(&num_b_stages, &pgc_lut->num_b_stages, sizeof(uint8_t))) return -EFAULT; r_data_temp32 = compat_ptr((uintptr_t)pgc_lut32->r_data); r_data_temp = pgc_lut->r_data; for (i = 0; i < num_r_stages; i++) { if (__to_user_ar_gc_lut_data( &r_data_temp32[i], &r_data_temp[i])) return -EFAULT; } g_data_temp32 = compat_ptr((uintptr_t)pgc_lut32->g_data); g_data_temp = pgc_lut->g_data; for (i = 0; i < num_g_stages; i++) { if (__to_user_ar_gc_lut_data( &g_data_temp32[i], &g_data_temp[i])) return -EFAULT; } b_data_temp32 = compat_ptr((uintptr_t)pgc_lut32->b_data); b_data_temp = pgc_lut->b_data; for (i = 0; i < num_b_stages; i++) { if (__to_user_ar_gc_lut_data( &b_data_temp32[i], &b_data_temp[i])) return -EFAULT; } return 0; } static int __from_user_hist_lut_data_v1_7( struct mdp_hist_lut_data32 __user *hist_lut32, struct mdp_hist_lut_data __user *hist_lut) { struct mdp_hist_lut_data_v1_7_32 hist_lut_cfg_payload32; struct mdp_hist_lut_data_v1_7 hist_lut_cfg_payload; if (copy_from_user(&hist_lut_cfg_payload32, compat_ptr(hist_lut32->cfg_payload), sizeof(hist_lut_cfg_payload32))) { pr_err("failed to copy the Hist Lut payload from userspace\n"); return -EFAULT; } hist_lut_cfg_payload.len = hist_lut_cfg_payload32.len; hist_lut_cfg_payload.data = compat_ptr(hist_lut_cfg_payload32.data); if (copy_to_user(hist_lut->cfg_payload, &hist_lut_cfg_payload, sizeof(hist_lut_cfg_payload))) { pr_err("Failed to copy to user hist lut cfg payload\n"); return -EFAULT; } return 0; } static int __from_user_hist_lut_data( struct mdp_hist_lut_data32 __user *hist_lut32, struct mdp_hist_lut_data __user *hist_lut) { uint32_t version = 0; uint32_t data; if (copy_in_user(&hist_lut->block, &hist_lut32->block, sizeof(uint32_t)) || copy_in_user(&hist_lut->version, &hist_lut32->version, sizeof(uint32_t)) || copy_in_user(&hist_lut->hist_lut_first, &hist_lut32->hist_lut_first, sizeof(uint32_t)) || copy_in_user(&hist_lut->ops, &hist_lut32->ops, sizeof(uint32_t)) || copy_in_user(&hist_lut->len, &hist_lut32->len, sizeof(uint32_t))) return -EFAULT; if (copy_from_user(&version, &hist_lut32->version, sizeof(uint32_t))) { pr_err("failed to copy the version info\n"); return -EFAULT; } switch (version) { case mdp_hist_lut_v1_7: if (__from_user_hist_lut_data_v1_7(hist_lut32, hist_lut)) { pr_err("failed to get hist lut data for version %d\n", version); return -EFAULT; } break; default: pr_debug("version invalid, fallback to legacy\n"); if (get_user(data, &hist_lut32->data) || put_user(compat_ptr(data), &hist_lut->data)) return -EFAULT; break; } return 0; } static int __to_user_hist_lut_data( struct mdp_hist_lut_data32 __user *hist_lut32, struct mdp_hist_lut_data __user *hist_lut) { unsigned long data; if (copy_in_user(&hist_lut32->block, &hist_lut->block, sizeof(uint32_t)) || copy_in_user(&hist_lut32->ops, &hist_lut->ops, sizeof(uint32_t)) || copy_in_user(&hist_lut32->len, &hist_lut->len, sizeof(uint32_t))) return -EFAULT; if (get_user(data, (unsigned long *) &hist_lut->data) || put_user((compat_caddr_t) data, &hist_lut32->data)) return -EFAULT; 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) { uint32_t lut_type; int ret = 0; if (copy_from_user(&lut_type, &lut_cfg32->lut_type, sizeof(uint32_t))) return -EFAULT; if (copy_in_user(&lut_cfg->lut_type, &lut_cfg32->lut_type, sizeof(uint32_t))) return -EFAULT; switch (lut_type) { case mdp_lut_igc: ret = __from_user_igc_lut_data( compat_ptr((uintptr_t)&lut_cfg32->data.igc_lut_data), &lut_cfg->data.igc_lut_data); break; case mdp_lut_pgc: ret = __from_user_pgc_lut_data( compat_ptr((uintptr_t)&lut_cfg32->data.pgc_lut_data), &lut_cfg->data.pgc_lut_data); break; case mdp_lut_hist: ret = __from_user_hist_lut_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; } return ret; } static int __to_user_lut_cfg_data( struct mdp_lut_cfg_data32 __user *lut_cfg32, struct mdp_lut_cfg_data __user *lut_cfg) { uint32_t lut_type; int ret = 0; if (copy_from_user(&lut_type, &lut_cfg->lut_type, sizeof(uint32_t))) return -EFAULT; if (copy_in_user(&lut_cfg32->lut_type, &lut_cfg->lut_type, sizeof(uint32_t))) return -EFAULT; switch (lut_type) { case mdp_lut_igc: ret = __to_user_igc_lut_data( compat_ptr((uintptr_t)&lut_cfg32->data.igc_lut_data), &lut_cfg->data.igc_lut_data); break; case mdp_lut_pgc: ret = __to_user_pgc_lut_data( compat_ptr((uintptr_t)&lut_cfg32->data.pgc_lut_data), &lut_cfg->data.pgc_lut_data); break; case mdp_lut_hist: ret = __to_user_hist_lut_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; } return ret; } static int __from_user_qseed_cfg( struct mdp_qseed_cfg32 __user *qseed_data32, struct mdp_qseed_cfg __user *qseed_data) { uint32_t data; if (copy_in_user(&qseed_data->table_num, &qseed_data32->table_num, sizeof(uint32_t)) || copy_in_user(&qseed_data->ops, &qseed_data32->ops, sizeof(uint32_t)) || copy_in_user(&qseed_data->len, &qseed_data32->len, sizeof(uint32_t))) return -EFAULT; if (get_user(data, &qseed_data32->data) || put_user(compat_ptr(data), &qseed_data->data)) return -EFAULT; return 0; } static int __to_user_qseed_cfg( struct mdp_qseed_cfg32 __user *qseed_data32, struct mdp_qseed_cfg __user *qseed_data) { unsigned long data; if (copy_in_user(&qseed_data32->table_num, &qseed_data->table_num, sizeof(uint32_t)) || copy_in_user(&qseed_data32->ops, &qseed_data->ops, sizeof(uint32_t)) || copy_in_user(&qseed_data32->len, &qseed_data->len, sizeof(uint32_t))) return -EFAULT; if (get_user(data, (unsigned long *) &qseed_data->data) || put_user((compat_caddr_t) data, &qseed_data32->data)) return -EFAULT; return 0; } static int __from_user_qseed_cfg_data( struct mdp_qseed_cfg_data32 __user *qseed_cfg32, struct mdp_qseed_cfg_data __user *qseed_cfg) { if (copy_in_user(&qseed_cfg->block, &qseed_cfg32->block, sizeof(uint32_t))) return -EFAULT; if (__from_user_qseed_cfg( compat_ptr((uintptr_t)&qseed_cfg32->qseed_data), &qseed_cfg->qseed_data)) return -EFAULT; return 0; } static int __to_user_qseed_cfg_data( struct mdp_qseed_cfg_data32 __user *qseed_cfg32, struct mdp_qseed_cfg_data __user *qseed_cfg) { if (copy_in_user(&qseed_cfg32->block, &qseed_cfg->block, sizeof(uint32_t))) return -EFAULT; if (__to_user_qseed_cfg( compat_ptr((uintptr_t)&qseed_cfg32->qseed_data), &qseed_cfg->qseed_data)) return -EFAULT; return 0; } static int __from_user_bl_scale_data( struct mdp_bl_scale_data32 __user *bl_scale32, struct mdp_bl_scale_data __user *bl_scale) { if (copy_in_user(&bl_scale->min_lvl, &bl_scale32->min_lvl, sizeof(uint32_t)) || copy_in_user(&bl_scale->scale, &bl_scale32->scale, sizeof(uint32_t))) return -EFAULT; return 0; } static int __from_user_pa_cfg( struct mdp_pa_cfg32 __user *pa_data32, struct mdp_pa_cfg __user *pa_data) { if (copy_in_user(&pa_data->flags, &pa_data32->flags, sizeof(uint32_t)) || copy_in_user(&pa_data->hue_adj, &pa_data32->hue_adj, sizeof(uint32_t)) || copy_in_user(&pa_data->sat_adj, &pa_data32->sat_adj, sizeof(uint32_t)) || copy_in_user(&pa_data->val_adj, &pa_data32->val_adj, sizeof(uint32_t)) || copy_in_user(&pa_data->cont_adj, &pa_data32->cont_adj, sizeof(uint32_t))) return -EFAULT; return 0; } static int __to_user_pa_cfg( struct mdp_pa_cfg32 __user *pa_data32, struct mdp_pa_cfg __user *pa_data) { if (copy_in_user(&pa_data32->flags, &pa_data->flags, sizeof(uint32_t)) || copy_in_user(&pa_data32->hue_adj, &pa_data->hue_adj, sizeof(uint32_t)) || copy_in_user(&pa_data32->sat_adj, &pa_data->sat_adj, sizeof(uint32_t)) || copy_in_user(&pa_data32->val_adj, &pa_data->val_adj, sizeof(uint32_t)) || copy_in_user(&pa_data32->cont_adj, &pa_data->cont_adj, sizeof(uint32_t))) return -EFAULT; return 0; } static int __from_user_pa_cfg_data( struct mdp_pa_cfg_data32 __user *pa_cfg32, struct mdp_pa_cfg_data __user *pa_cfg) { if (copy_in_user(&pa_cfg->block, &pa_cfg32->block, sizeof(uint32_t))) return -EFAULT; if (__from_user_pa_cfg( compat_ptr((uintptr_t)&pa_cfg32->pa_data), &pa_cfg->pa_data)) return -EFAULT; return 0; } static int __to_user_pa_cfg_data( struct mdp_pa_cfg_data32 __user *pa_cfg32, struct mdp_pa_cfg_data __user *pa_cfg) { if (copy_in_user(&pa_cfg32->block, &pa_cfg->block, sizeof(uint32_t))) return -EFAULT; if (__to_user_pa_cfg( compat_ptr((uintptr_t)&pa_cfg32->pa_data), &pa_cfg->pa_data)) return -EFAULT; return 0; } static int __from_user_mem_col_cfg( struct mdp_pa_mem_col_cfg32 __user *mem_col_cfg32, struct mdp_pa_mem_col_cfg __user *mem_col_cfg) { if (copy_in_user(&mem_col_cfg->color_adjust_p0, &mem_col_cfg32->color_adjust_p0, sizeof(uint32_t)) || copy_in_user(&mem_col_cfg->color_adjust_p1, &mem_col_cfg32->color_adjust_p1, sizeof(uint32_t)) || copy_in_user(&mem_col_cfg->hue_region, &mem_col_cfg32->hue_region, sizeof(uint32_t)) || copy_in_user(&mem_col_cfg->sat_region, &mem_col_cfg32->sat_region, sizeof(uint32_t)) || copy_in_user(&mem_col_cfg->val_region, &mem_col_cfg32->val_region, sizeof(uint32_t))) return -EFAULT; return 0; } static int __to_user_mem_col_cfg( struct mdp_pa_mem_col_cfg32 __user *mem_col_cfg32, struct mdp_pa_mem_col_cfg __user *mem_col_cfg) { if (copy_in_user(&mem_col_cfg32->color_adjust_p0, &mem_col_cfg->color_adjust_p0, sizeof(uint32_t)) || copy_in_user(&mem_col_cfg32->color_adjust_p1, &mem_col_cfg->color_adjust_p1, sizeof(uint32_t)) || copy_in_user(&mem_col_cfg32->hue_region, &mem_col_cfg->hue_region, sizeof(uint32_t)) || copy_in_user(&mem_col_cfg32->sat_region, &mem_col_cfg->sat_region, sizeof(uint32_t)) || copy_in_user(&mem_col_cfg32->val_region, &mem_col_cfg->val_region, sizeof(uint32_t))) return -EFAULT; return 0; } static int __from_user_pa_v2_data( struct mdp_pa_v2_data32 __user *pa_v2_data32, struct mdp_pa_v2_data __user *pa_v2_data) { uint32_t data; if (copy_in_user(&pa_v2_data->flags, &pa_v2_data32->flags, sizeof(uint32_t)) || copy_in_user(&pa_v2_data->global_hue_adj, &pa_v2_data32->global_hue_adj, sizeof(uint32_t)) || copy_in_user(&pa_v2_data->global_sat_adj, &pa_v2_data32->global_sat_adj, sizeof(uint32_t)) || copy_in_user(&pa_v2_data->global_val_adj, &pa_v2_data32->global_val_adj, sizeof(uint32_t)) || copy_in_user(&pa_v2_data->global_cont_adj, &pa_v2_data32->global_cont_adj, sizeof(uint32_t)) || copy_in_user(&pa_v2_data->six_zone_thresh, &pa_v2_data32->six_zone_thresh, sizeof(uint32_t)) || copy_in_user(&pa_v2_data->six_zone_len, &pa_v2_data32->six_zone_len, sizeof(uint32_t))) return -EFAULT; if (get_user(data, &pa_v2_data32->six_zone_curve_p0) || put_user(compat_ptr(data), &pa_v2_data->six_zone_curve_p0) || get_user(data, &pa_v2_data32->six_zone_curve_p1) || put_user(compat_ptr(data), &pa_v2_data->six_zone_curve_p1)) return -EFAULT; if (__from_user_mem_col_cfg( compat_ptr((uintptr_t)&pa_v2_data32->skin_cfg), &pa_v2_data->skin_cfg) || __from_user_mem_col_cfg( compat_ptr((uintptr_t)&pa_v2_data32->sky_cfg), &pa_v2_data->sky_cfg) || __from_user_mem_col_cfg( compat_ptr((uintptr_t)&pa_v2_data32->fol_cfg), &pa_v2_data->fol_cfg)) return -EFAULT; return 0; } static int __to_user_pa_v2_data( struct mdp_pa_v2_data32 __user *pa_v2_data32, struct mdp_pa_v2_data __user *pa_v2_data) { unsigned long data; if (copy_in_user(&pa_v2_data32->flags, &pa_v2_data->flags, sizeof(uint32_t)) || copy_in_user(&pa_v2_data32->global_hue_adj, &pa_v2_data->global_hue_adj, sizeof(uint32_t)) || copy_in_user(&pa_v2_data32->global_sat_adj, &pa_v2_data->global_sat_adj, sizeof(uint32_t)) || copy_in_user(&pa_v2_data32->global_val_adj, &pa_v2_data->global_val_adj, sizeof(uint32_t)) || copy_in_user(&pa_v2_data32->global_cont_adj, &pa_v2_data->global_cont_adj, sizeof(uint32_t)) || copy_in_user(&pa_v2_data32->six_zone_thresh, &pa_v2_data->six_zone_thresh, sizeof(uint32_t)) || copy_in_user(&pa_v2_data32->six_zone_len, &pa_v2_data->six_zone_len, sizeof(uint32_t))) return -EFAULT; if (get_user(data, (unsigned long *) &pa_v2_data->six_zone_curve_p0) || put_user((compat_caddr_t) data, &pa_v2_data32->six_zone_curve_p0) || get_user(data, (unsigned long *) &pa_v2_data->six_zone_curve_p1) || put_user((compat_caddr_t) data, &pa_v2_data32->six_zone_curve_p1)) return -EFAULT; if (__to_user_mem_col_cfg( compat_ptr((uintptr_t)&pa_v2_data32->skin_cfg), &pa_v2_data->skin_cfg) || __to_user_mem_col_cfg( compat_ptr((uintptr_t)&pa_v2_data32->sky_cfg), &pa_v2_data->sky_cfg) || __to_user_mem_col_cfg( compat_ptr((uintptr_t)&pa_v2_data32->fol_cfg), &pa_v2_data->fol_cfg)) return -EFAULT; return 0; } static inline void __from_user_pa_mem_col_data_v1_7( struct mdp_pa_mem_col_data_v1_7_32 *mem_col_data32, struct mdp_pa_mem_col_data_v1_7 *mem_col_data) { mem_col_data->color_adjust_p0 = mem_col_data32->color_adjust_p0; mem_col_data->color_adjust_p1 = mem_col_data32->color_adjust_p1; mem_col_data->color_adjust_p2 = mem_col_data32->color_adjust_p2; mem_col_data->blend_gain = mem_col_data32->blend_gain; mem_col_data->sat_hold = mem_col_data32->sat_hold; mem_col_data->val_hold = mem_col_data32->val_hold; mem_col_data->hue_region = mem_col_data32->hue_region; mem_col_data->sat_region = mem_col_data32->sat_region; mem_col_data->val_region = mem_col_data32->val_region; } static int __from_user_pa_data_v1_7( struct mdp_pa_v2_cfg_data32 __user *pa_v2_cfg32, struct mdp_pa_v2_cfg_data __user *pa_v2_cfg) { struct mdp_pa_data_v1_7_32 pa_cfg_payload32; struct mdp_pa_data_v1_7 pa_cfg_payload; if (copy_from_user(&pa_cfg_payload32, compat_ptr(pa_v2_cfg32->cfg_payload), sizeof(pa_cfg_payload32))) { pr_err("failed to copy the PA payload from userspace\n"); return -EFAULT; } pa_cfg_payload.mode = pa_cfg_payload32.mode; pa_cfg_payload.global_hue_adj = pa_cfg_payload32.global_hue_adj; pa_cfg_payload.global_sat_adj = pa_cfg_payload32.global_sat_adj; pa_cfg_payload.global_val_adj = pa_cfg_payload32.global_val_adj; pa_cfg_payload.global_cont_adj = pa_cfg_payload32.global_cont_adj; __from_user_pa_mem_col_data_v1_7(&pa_cfg_payload32.skin_cfg, &pa_cfg_payload.skin_cfg); __from_user_pa_mem_col_data_v1_7(&pa_cfg_payload32.sky_cfg, &pa_cfg_payload.sky_cfg); __from_user_pa_mem_col_data_v1_7(&pa_cfg_payload32.fol_cfg, &pa_cfg_payload.fol_cfg); pa_cfg_payload.six_zone_thresh = pa_cfg_payload32.six_zone_thresh; pa_cfg_payload.six_zone_adj_p0 = pa_cfg_payload32.six_zone_adj_p0; pa_cfg_payload.six_zone_adj_p1 = pa_cfg_payload32.six_zone_adj_p1; pa_cfg_payload.six_zone_sat_hold = pa_cfg_payload32.six_zone_sat_hold; pa_cfg_payload.six_zone_val_hold = pa_cfg_payload32.six_zone_val_hold; pa_cfg_payload.six_zone_len = pa_cfg_payload32.six_zone_len; pa_cfg_payload.six_zone_curve_p0 = compat_ptr(pa_cfg_payload32.six_zone_curve_p0); pa_cfg_payload.six_zone_curve_p1 = compat_ptr(pa_cfg_payload32.six_zone_curve_p1); if (copy_to_user(pa_v2_cfg->cfg_payload, &pa_cfg_payload, sizeof(pa_cfg_payload))) { pr_err("Failed to copy to user pa cfg payload\n"); return -EFAULT; } return 0; } static int __from_user_pa_v2_cfg_data( struct mdp_pa_v2_cfg_data32 __user *pa_v2_cfg32, struct mdp_pa_v2_cfg_data __user *pa_v2_cfg) { uint32_t version; if (copy_in_user(&pa_v2_cfg->block, &pa_v2_cfg32->block, sizeof(uint32_t)) || copy_in_user(&pa_v2_cfg->version, &pa_v2_cfg32->version, sizeof(uint32_t)) || copy_in_user(&pa_v2_cfg->flags, &pa_v2_cfg32->flags, sizeof(uint32_t))) return -EFAULT; if (copy_from_user(&version, &pa_v2_cfg32->version, sizeof(uint32_t))) { pr_err("failed to copy the version info\n"); return -EFAULT; } switch (version) { case mdp_pa_v1_7: if (__from_user_pa_data_v1_7(pa_v2_cfg32, pa_v2_cfg)) { pr_err("failed to get pa data for version %d\n", version); return -EFAULT; } break; default: pr_debug("version invalid, fallback to legacy\n"); if (__from_user_pa_v2_data( compat_ptr((uintptr_t)&pa_v2_cfg32->pa_v2_data), &pa_v2_cfg->pa_v2_data)) return -EFAULT; break; } return 0; } static inline void __to_user_pa_mem_col_data_v1_7( struct mdp_pa_mem_col_data_v1_7_32 *mem_col_data32, struct mdp_pa_mem_col_data_v1_7 *mem_col_data) { mem_col_data32->color_adjust_p0 = mem_col_data->color_adjust_p0; mem_col_data32->color_adjust_p1 = mem_col_data->color_adjust_p1; mem_col_data32->color_adjust_p2 = mem_col_data->color_adjust_p2; mem_col_data32->blend_gain = mem_col_data->blend_gain; mem_col_data32->sat_hold = mem_col_data->sat_hold; mem_col_data32->val_hold = mem_col_data->val_hold; mem_col_data32->hue_region = mem_col_data->hue_region; mem_col_data32->sat_region = mem_col_data->sat_region; mem_col_data32->val_region = mem_col_data->val_region; } static int __to_user_pa_data_v1_7( struct mdp_pa_v2_cfg_data32 __user *pa_v2_cfg32, struct mdp_pa_v2_cfg_data __user *pa_v2_cfg) { struct mdp_pa_data_v1_7_32 pa_cfg_payload32; struct mdp_pa_data_v1_7 pa_cfg_payload; if (copy_from_user(&pa_cfg_payload, pa_v2_cfg->cfg_payload, sizeof(pa_cfg_payload))) { pr_err("failed to copy the PA payload from userspace\n"); return -EFAULT; } pa_cfg_payload32.mode = pa_cfg_payload.mode; pa_cfg_payload32.global_hue_adj = pa_cfg_payload.global_hue_adj; pa_cfg_payload32.global_sat_adj = pa_cfg_payload.global_sat_adj; pa_cfg_payload32.global_val_adj = pa_cfg_payload.global_val_adj; pa_cfg_payload32.global_cont_adj = pa_cfg_payload.global_cont_adj; __to_user_pa_mem_col_data_v1_7(&pa_cfg_payload32.skin_cfg, &pa_cfg_payload.skin_cfg); __to_user_pa_mem_col_data_v1_7(&pa_cfg_payload32.sky_cfg, &pa_cfg_payload.sky_cfg); __to_user_pa_mem_col_data_v1_7(&pa_cfg_payload32.fol_cfg, &pa_cfg_payload.fol_cfg); pa_cfg_payload32.six_zone_thresh = pa_cfg_payload.six_zone_thresh; pa_cfg_payload32.six_zone_adj_p0 = pa_cfg_payload.six_zone_adj_p0; pa_cfg_payload32.six_zone_adj_p1 = pa_cfg_payload.six_zone_adj_p1; pa_cfg_payload32.six_zone_sat_hold = pa_cfg_payload.six_zone_sat_hold; pa_cfg_payload32.six_zone_val_hold = pa_cfg_payload.six_zone_val_hold; pa_cfg_payload32.six_zone_len = pa_cfg_payload.six_zone_len; if (copy_to_user(compat_ptr(pa_v2_cfg32->cfg_payload), &pa_cfg_payload32, sizeof(pa_cfg_payload32))) { pr_err("Failed to copy to user pa cfg payload\n"); return -EFAULT; } return 0; } static int __to_user_pa_v2_cfg_data( struct mdp_pa_v2_cfg_data32 __user *pa_v2_cfg32, struct mdp_pa_v2_cfg_data __user *pa_v2_cfg) { uint32_t version = 0; uint32_t flags = 0; if (copy_from_user(&version, &pa_v2_cfg32->version, sizeof(uint32_t))) return -EFAULT; switch (version) { case mdp_pa_v1_7: if (copy_from_user(&flags, &pa_v2_cfg32->flags, sizeof(uint32_t))) { pr_err("failed to get PA v1_7 flags\n"); return -EFAULT; } if (!(flags & MDP_PP_OPS_READ)) { pr_debug("Read op not set. Skipping compat copyback\n"); return 0; } if (__to_user_pa_data_v1_7(pa_v2_cfg32, pa_v2_cfg)) { pr_err("failed to set pa data for version %d\n", version); return -EFAULT; } break; default: pr_debug("version invalid, fallback to legacy\n"); if (copy_from_user(&flags, &pa_v2_cfg32->pa_v2_data.flags, sizeof(uint32_t))) { pr_err("failed to get PAv2 flags\n"); return -EFAULT; } if (!(flags & MDP_PP_OPS_READ)) { pr_debug("Read op not set. Skipping compat copyback\n"); return 0; } if (__to_user_pa_v2_data( compat_ptr((uintptr_t)&pa_v2_cfg32->pa_v2_data), &pa_v2_cfg->pa_v2_data)) return -EFAULT; break; } return 0; } static int __from_user_dither_cfg_data( struct mdp_dither_cfg_data32 __user *dither_cfg32, struct mdp_dither_cfg_data __user *dither_cfg) { if (copy_in_user(&dither_cfg->block, &dither_cfg32->block, sizeof(uint32_t)) || copy_in_user(&dither_cfg->flags, &dither_cfg32->flags, sizeof(uint32_t)) || copy_in_user(&dither_cfg->g_y_depth, &dither_cfg32->g_y_depth, sizeof(uint32_t)) || copy_in_user(&dither_cfg->r_cr_depth, &dither_cfg32->r_cr_depth, sizeof(uint32_t)) || copy_in_user(&dither_cfg->b_cb_depth, &dither_cfg32->b_cb_depth, sizeof(uint32_t))) return -EFAULT; return 0; } static int __to_user_dither_cfg_data( struct mdp_dither_cfg_data32 __user *dither_cfg32, struct mdp_dither_cfg_data __user *dither_cfg) { if (copy_in_user(&dither_cfg32->block, &dither_cfg->block, sizeof(uint32_t)) || copy_in_user(&dither_cfg32->flags, &dither_cfg->flags, sizeof(uint32_t)) || copy_in_user(&dither_cfg32->g_y_depth, &dither_cfg->g_y_depth, sizeof(uint32_t)) || copy_in_user(&dither_cfg32->r_cr_depth, &dither_cfg->r_cr_depth, sizeof(uint32_t)) || copy_in_user(&dither_cfg32->b_cb_depth, &dither_cfg->b_cb_depth, sizeof(uint32_t))) return -EFAULT; return 0; } static int __from_user_gamut_cfg_data_v17( struct mdp_gamut_cfg_data32 __user *gamut_cfg32, struct mdp_gamut_cfg_data __user *gamut_cfg) { struct mdp_gamut_data_v1_7 gamut_cfg_payload; struct mdp_gamut_data_v1_7_32 gamut_cfg_payload32; u32 i = 0; if (copy_from_user(&gamut_cfg_payload32, compat_ptr(gamut_cfg32->cfg_payload), sizeof(gamut_cfg_payload32))) { pr_err("failed to copy the gamut payload from userspace\n"); return -EFAULT; } gamut_cfg_payload.mode = gamut_cfg_payload32.mode; for (i = 0; i < MDP_GAMUT_TABLE_NUM_V1_7; i++) { gamut_cfg_payload.tbl_size[i] = gamut_cfg_payload32.tbl_size[i]; gamut_cfg_payload.c0_data[i] = compat_ptr(gamut_cfg_payload32.c0_data[i]); gamut_cfg_payload.c1_c2_data[i] = compat_ptr(gamut_cfg_payload32.c1_c2_data[i]); } for (i = 0; i < MDP_GAMUT_SCALE_OFF_TABLE_NUM; i++) { gamut_cfg_payload.tbl_scale_off_sz[i] = gamut_cfg_payload32.tbl_scale_off_sz[i]; gamut_cfg_payload.scale_off_data[i] = compat_ptr(gamut_cfg_payload32.scale_off_data[i]); } if (copy_to_user(gamut_cfg->cfg_payload, &gamut_cfg_payload, sizeof(gamut_cfg_payload))) { pr_err("failed to copy the gamut payload to userspace\n"); return -EFAULT; } return 0; } static int __from_user_gamut_cfg_data( struct mdp_gamut_cfg_data32 __user *gamut_cfg32, struct mdp_gamut_cfg_data __user *gamut_cfg) { uint32_t data, version; int i; if (copy_in_user(&gamut_cfg->block, &gamut_cfg32->block, sizeof(uint32_t)) || copy_in_user(&gamut_cfg->flags, &gamut_cfg32->flags, sizeof(uint32_t)) || copy_in_user(&gamut_cfg->gamut_first, &gamut_cfg32->gamut_first, sizeof(uint32_t)) || copy_in_user(&gamut_cfg->tbl_size[0], &gamut_cfg32->tbl_size[0], MDP_GAMUT_TABLE_NUM * sizeof(uint32_t)) || copy_in_user(&gamut_cfg->version, &gamut_cfg32->version, sizeof(uint32_t))) return 0; if (copy_from_user(&version, &gamut_cfg32->version, sizeof(u32))) { pr_err("failed to copy the version info\n"); return -EFAULT; } switch (version) { case mdp_gamut_v1_7: if (__from_user_gamut_cfg_data_v17(gamut_cfg32, gamut_cfg)) { pr_err("failed to get the gamut data for version %d\n", version); return -EFAULT; } break; default: pr_debug("version invalid fallback to legacy\n"); /* The Gamut LUT data contains 3 static arrays for R, G, and B * gamut data. Each these arrays contains pointers dynamic arrays * which hold the gamut LUTs for R, G, and B. Must copy the array of * pointers from 32 bit to 64 bit addresses. */ for (i = 0; i < MDP_GAMUT_TABLE_NUM; i++) { if (get_user(data, &gamut_cfg32->r_tbl[i]) || put_user(compat_ptr(data), &gamut_cfg->r_tbl[i])) return -EFAULT; } for (i = 0; i < MDP_GAMUT_TABLE_NUM; i++) { if (get_user(data, &gamut_cfg32->g_tbl[i]) || put_user(compat_ptr(data), &gamut_cfg->g_tbl[i])) return -EFAULT; } for (i = 0; i < MDP_GAMUT_TABLE_NUM; i++) { if (get_user(data, &gamut_cfg32->b_tbl[i]) || put_user(compat_ptr(data), &gamut_cfg->b_tbl[i])) return -EFAULT; } break; } return 0; } static int __to_user_gamut_cfg_data( struct mdp_gamut_cfg_data32 __user *gamut_cfg32, struct mdp_gamut_cfg_data __user *gamut_cfg) { unsigned long data; int i; if (copy_in_user(&gamut_cfg32->block, &gamut_cfg->block, sizeof(uint32_t)) || copy_in_user(&gamut_cfg32->flags, &gamut_cfg->flags, sizeof(uint32_t)) || copy_in_user(&gamut_cfg32->gamut_first, &gamut_cfg->gamut_first, sizeof(uint32_t)) || copy_in_user(&gamut_cfg32->tbl_size[0], &gamut_cfg->tbl_size[0], MDP_GAMUT_TABLE_NUM * sizeof(uint32_t))) return 0; for (i = 0; i < MDP_GAMUT_TABLE_NUM; i++) { if (get_user(data, (unsigned long *) &gamut_cfg->r_tbl[i]) || put_user((compat_caddr_t)data, &gamut_cfg32->r_tbl[i])) return -EFAULT; } for (i = 0; i < MDP_GAMUT_TABLE_NUM; i++) { if (get_user(data, (unsigned long *) &gamut_cfg->g_tbl[i]) || put_user((compat_caddr_t)data, &gamut_cfg32->g_tbl[i])) return -EFAULT; } for (i = 0; i < MDP_GAMUT_TABLE_NUM; i++) { if (get_user(data, (unsigned long *) &gamut_cfg->b_tbl[i]) || put_user((compat_caddr_t)data, &gamut_cfg32->g_tbl[i])) return -EFAULT; } return 0; } static int __from_user_calib_config_data( struct mdp_calib_config_data32 __user *calib_cfg32, struct mdp_calib_config_data __user *calib_cfg) { if (copy_in_user(&calib_cfg->ops, &calib_cfg32->ops, sizeof(uint32_t)) || copy_in_user(&calib_cfg->addr, &calib_cfg32->addr, sizeof(uint32_t)) || copy_in_user(&calib_cfg->data, &calib_cfg32->data, sizeof(uint32_t))) return -EFAULT; return 0; } static int __to_user_calib_config_data( struct mdp_calib_config_data32 __user *calib_cfg32, struct mdp_calib_config_data __user *calib_cfg) { if (copy_in_user(&calib_cfg32->ops, &calib_cfg->ops, sizeof(uint32_t)) || copy_in_user(&calib_cfg32->addr, &calib_cfg->addr, sizeof(uint32_t)) || copy_in_user(&calib_cfg32->data, &calib_cfg->data, sizeof(uint32_t))) return -EFAULT; return 0; } static int __from_user_ad_init( struct mdss_ad_init32 __user *ad_init32, struct mdss_ad_init __user *ad_init) { uint32_t data; if (copy_in_user(&ad_init->asym_lut[0], &ad_init32->asym_lut[0], 33 * sizeof(uint32_t)) || copy_in_user(&ad_init->color_corr_lut[0], &ad_init32->color_corr_lut[0], 33 * sizeof(uint32_t)) || copy_in_user(&ad_init->i_control[0], &ad_init32->i_control[0], 2 * sizeof(uint8_t)) || copy_in_user(&ad_init->black_lvl, &ad_init32->black_lvl, sizeof(uint16_t)) || copy_in_user(&ad_init->white_lvl, &ad_init32->white_lvl, sizeof(uint16_t)) || copy_in_user(&ad_init->var, &ad_init32->var, sizeof(uint8_t)) || copy_in_user(&ad_init->limit_ampl, &ad_init32->limit_ampl, sizeof(uint8_t)) || copy_in_user(&ad_init->i_dither, &ad_init32->i_dither, sizeof(uint8_t)) || copy_in_user(&ad_init->slope_max, &ad_init32->slope_max, sizeof(uint8_t)) || copy_in_user(&ad_init->slope_min, &ad_init32->slope_min, sizeof(uint8_t)) || copy_in_user(&ad_init->dither_ctl, &ad_init32->dither_ctl, sizeof(uint8_t)) || copy_in_user(&ad_init->format, &ad_init32->format, sizeof(uint8_t)) || copy_in_user(&ad_init->auto_size, &ad_init32->auto_size, sizeof(uint8_t)) || copy_in_user(&ad_init->frame_w, &ad_init32->frame_w, sizeof(uint16_t)) || copy_in_user(&ad_init->frame_h, &ad_init32->frame_h, sizeof(uint16_t)) || copy_in_user(&ad_init->logo_v, &ad_init32->logo_v, sizeof(uint8_t)) || copy_in_user(&ad_init->logo_h, &ad_init32->logo_h, sizeof(uint8_t)) || copy_in_user(&ad_init->alpha, &ad_init32->alpha, sizeof(uint32_t)) || copy_in_user(&ad_init->alpha_base, &ad_init32->alpha_base, sizeof(uint32_t)) || copy_in_user(&ad_init->bl_lin_len, &ad_init32->bl_lin_len, sizeof(uint32_t)) || copy_in_user(&ad_init->bl_att_len, &ad_init32->bl_att_len, sizeof(uint32_t))) return -EFAULT; if (get_user(data, &ad_init32->bl_lin) || put_user(compat_ptr(data), &ad_init->bl_lin) || get_user(data, &ad_init32->bl_lin_inv) || put_user(compat_ptr(data), &ad_init->bl_lin_inv) || get_user(data, &ad_init32->bl_att_lut) || put_user(compat_ptr(data), &ad_init->bl_att_lut)) return -EFAULT; return 0; } static int __from_user_ad_cfg( struct mdss_ad_cfg32 __user *ad_cfg32, struct mdss_ad_cfg __user *ad_cfg) { if (copy_in_user(&ad_cfg->mode, &ad_cfg32->mode, sizeof(uint32_t)) || copy_in_user(&ad_cfg->al_calib_lut[0], &ad_cfg32->al_calib_lut[0], 33 * sizeof(uint32_t)) || copy_in_user(&ad_cfg->backlight_min, &ad_cfg32->backlight_min, sizeof(uint16_t)) || copy_in_user(&ad_cfg->backlight_max, &ad_cfg32->backlight_max, sizeof(uint16_t)) || copy_in_user(&ad_cfg->backlight_scale, &ad_cfg32->backlight_scale, sizeof(uint16_t)) || copy_in_user(&ad_cfg->amb_light_min, &ad_cfg32->amb_light_min, sizeof(uint16_t)) || copy_in_user(&ad_cfg->filter[0], &ad_cfg32->filter[0], 2 * sizeof(uint16_t)) || copy_in_user(&ad_cfg->calib[0], &ad_cfg32->calib[0], 4 * sizeof(uint16_t)) || copy_in_user(&ad_cfg->strength_limit, &ad_cfg32->strength_limit, sizeof(uint8_t)) || copy_in_user(&ad_cfg->t_filter_recursion, &ad_cfg32->t_filter_recursion, sizeof(uint8_t)) || copy_in_user(&ad_cfg->stab_itr, &ad_cfg32->stab_itr, sizeof(uint16_t)) || copy_in_user(&ad_cfg->bl_ctrl_mode, &ad_cfg32->bl_ctrl_mode, sizeof(uint32_t))) return -EFAULT; return 0; } static int __from_user_ad_init_cfg( struct mdss_ad_init_cfg32 __user *ad_info32, struct mdss_ad_init_cfg __user *ad_info) { uint32_t op; if (copy_from_user(&op, &ad_info32->ops, sizeof(uint32_t))) return -EFAULT; if (copy_in_user(&ad_info->ops, &ad_info32->ops, sizeof(uint32_t))) return -EFAULT; if (op & MDP_PP_AD_INIT) { if (__from_user_ad_init( compat_ptr((uintptr_t)&ad_info32->params.init), &ad_info->params.init)) return -EFAULT; } else if (op & MDP_PP_AD_CFG) { if (__from_user_ad_cfg( compat_ptr((uintptr_t)&ad_info32->params.cfg), &ad_info->params.cfg)) return -EFAULT; } else { pr_err("Invalid AD init/config operation\n"); return -EINVAL; } return 0; } static int __from_user_ad_input( struct mdss_ad_input32 __user *ad_input32, struct mdss_ad_input __user *ad_input) { int mode; if (copy_from_user(&mode, &ad_input32->mode, sizeof(uint32_t))) return -EFAULT; if (copy_in_user(&ad_input->mode, &ad_input32->mode, sizeof(uint32_t)) || copy_in_user(&ad_input->output, &ad_input32->output, sizeof(uint32_t))) return -EFAULT; switch (mode) { case MDSS_AD_MODE_AUTO_BL: case MDSS_AD_MODE_AUTO_STR: if (copy_in_user(&ad_input->in.amb_light, &ad_input32->in.amb_light, sizeof(uint32_t))) return -EFAULT; break; case MDSS_AD_MODE_TARG_STR: case MDSS_AD_MODE_MAN_STR: if (copy_in_user(&ad_input->in.strength, &ad_input32->in.strength, sizeof(uint32_t))) return -EFAULT; break; case MDSS_AD_MODE_CALIB: if (copy_in_user(&ad_input->in.calib_bl, &ad_input32->in.calib_bl, sizeof(uint32_t))) return -EFAULT; break; } return 0; } static int __to_user_ad_input( struct mdss_ad_input32 __user *ad_input32, struct mdss_ad_input __user *ad_input) { int mode; if (copy_from_user(&mode, &ad_input->mode, sizeof(uint32_t))) return -EFAULT; if (copy_in_user(&ad_input32->mode, &ad_input->mode, sizeof(uint32_t)) || copy_in_user(&ad_input32->output, &ad_input->output, sizeof(uint32_t))) return -EFAULT; switch (mode) { case MDSS_AD_MODE_AUTO_BL: case MDSS_AD_MODE_AUTO_STR: if (copy_in_user(&ad_input32->in.amb_light, &ad_input->in.amb_light, sizeof(uint32_t))) return -EFAULT; break; case MDSS_AD_MODE_TARG_STR: case MDSS_AD_MODE_MAN_STR: if (copy_in_user(&ad_input32->in.strength, &ad_input->in.strength, sizeof(uint32_t))) return -EFAULT; break; case MDSS_AD_MODE_CALIB: if (copy_in_user(&ad_input32->in.calib_bl, &ad_input->in.calib_bl, sizeof(uint32_t))) return -EFAULT; break; } return 0; } static int __from_user_calib_cfg( struct mdss_calib_cfg32 __user *calib_cfg32, struct mdss_calib_cfg __user *calib_cfg) { if (copy_in_user(&calib_cfg->ops, &calib_cfg32->ops, sizeof(uint32_t)) || copy_in_user(&calib_cfg->calib_mask, &calib_cfg32->calib_mask, sizeof(uint32_t))) return -EFAULT; return 0; } static int __from_user_calib_config_buffer( struct mdp_calib_config_buffer32 __user *calib_buffer32, struct mdp_calib_config_buffer __user *calib_buffer) { uint32_t data; if (copy_in_user(&calib_buffer->ops, &calib_buffer32->ops, sizeof(uint32_t)) || copy_in_user(&calib_buffer->size, &calib_buffer32->size, sizeof(uint32_t))) return -EFAULT; if (get_user(data, &calib_buffer32->buffer) || put_user(compat_ptr(data), &calib_buffer->buffer)) return -EFAULT; return 0; } static int __to_user_calib_config_buffer( struct mdp_calib_config_buffer32 __user *calib_buffer32, struct mdp_calib_config_buffer __user *calib_buffer) { unsigned long data; if (copy_in_user(&calib_buffer32->ops, &calib_buffer->ops, sizeof(uint32_t)) || copy_in_user(&calib_buffer32->size, &calib_buffer->size, sizeof(uint32_t))) return -EFAULT; if (get_user(data, (unsigned long *) &calib_buffer->buffer) || put_user((compat_caddr_t) data, &calib_buffer32->buffer)) return -EFAULT; return 0; } static int __from_user_calib_dcm_state( struct mdp_calib_dcm_state32 __user *calib_dcm32, struct mdp_calib_dcm_state __user *calib_dcm) { if (copy_in_user(&calib_dcm->ops, &calib_dcm32->ops, sizeof(uint32_t)) || copy_in_user(&calib_dcm->dcm_state, &calib_dcm32->dcm_state, sizeof(uint32_t))) return -EFAULT; return 0; } static u32 __pp_compat_size_igc(void) { u32 alloc_size = 0; /* When we have mutiple versions pick largest struct size */ alloc_size = sizeof(struct mdp_igc_lut_data_v1_7); return alloc_size; } static u32 __pp_compat_size_hist_lut(void) { u32 alloc_size = 0; /* When we have mutiple versions pick largest struct size */ alloc_size = sizeof(struct mdp_hist_lut_data_v1_7); return alloc_size; } static u32 __pp_compat_size_pgc(void) { u32 tbl_sz_max = 0; tbl_sz_max = 3 * GC_LUT_SEGMENTS * sizeof(struct mdp_ar_gc_lut_data); tbl_sz_max += sizeof(struct mdp_pgc_lut_data_v1_7); return tbl_sz_max; } static u32 __pp_compat_size_pcc(void) { /* if new version of PCC is added return max struct size */ return sizeof(struct mdp_pcc_data_v1_7); } static u32 __pp_compat_size_pa(void) { /* if new version of PA is added return max struct size */ return sizeof(struct mdp_pa_data_v1_7); } static u32 __pp_compat_size_gamut(void) { return sizeof(struct mdp_gamut_data_v1_7); } static int __pp_compat_alloc(struct msmfb_mdp_pp32 __user *pp32, struct msmfb_mdp_pp __user **pp, uint32_t op) { uint32_t alloc_size = 0, lut_type, pgc_size = 0; alloc_size = sizeof(struct msmfb_mdp_pp); switch (op) { case mdp_op_lut_cfg: if (copy_from_user(&lut_type, &pp32->data.lut_cfg_data.lut_type, sizeof(uint32_t))) return -EFAULT; switch (lut_type) { case mdp_lut_pgc: pgc_size = GC_LUT_SEGMENTS * sizeof(struct mdp_ar_gc_lut_data); alloc_size += __pp_compat_size_pgc(); *pp = compat_alloc_user_space(alloc_size); if (NULL == *pp) return -ENOMEM; memset(*pp, 0, alloc_size); (*pp)->data.lut_cfg_data.data.pgc_lut_data.r_data = (struct mdp_ar_gc_lut_data *) ((unsigned long) *pp + sizeof(struct msmfb_mdp_pp)); (*pp)->data.lut_cfg_data.data.pgc_lut_data.g_data = (struct mdp_ar_gc_lut_data *) ((unsigned long) *pp + sizeof(struct msmfb_mdp_pp) + pgc_size); (*pp)->data.lut_cfg_data.data.pgc_lut_data.b_data = (struct mdp_ar_gc_lut_data *) ((unsigned long) *pp + sizeof(struct msmfb_mdp_pp) + (2 * pgc_size)); (*pp)->data.lut_cfg_data.data.pgc_lut_data.cfg_payload = (void *)((unsigned long) *pp + sizeof(struct msmfb_mdp_pp) + (3 * pgc_size)); break; case mdp_lut_igc: alloc_size += __pp_compat_size_igc(); *pp = compat_alloc_user_space(alloc_size); if (NULL == *pp) { pr_err("failed to alloc from user size %d for igc\n", alloc_size); return -ENOMEM; } memset(*pp, 0, alloc_size); (*pp)->data.lut_cfg_data.data.igc_lut_data.cfg_payload = (void *)((unsigned long)(*pp) + sizeof(struct msmfb_mdp_pp)); break; case mdp_lut_hist: alloc_size += __pp_compat_size_hist_lut(); *pp = compat_alloc_user_space(alloc_size); if (NULL == *pp) { pr_err("failed to alloc from user size %d for hist lut\n", alloc_size); return -ENOMEM; } memset(*pp, 0, alloc_size); (*pp)->data.lut_cfg_data.data.hist_lut_data.cfg_payload = (void *)((unsigned long)(*pp) + sizeof(struct msmfb_mdp_pp)); break; default: *pp = compat_alloc_user_space(alloc_size); if (NULL == *pp) { pr_err("failed to alloc from user size %d for lut_type %d\n", alloc_size, lut_type); return -ENOMEM; } memset(*pp, 0, alloc_size); break; } break; case mdp_op_pcc_cfg: alloc_size += __pp_compat_size_pcc(); *pp = compat_alloc_user_space(alloc_size); if (NULL == *pp) { pr_err("alloc from user size %d for pcc fail\n", alloc_size); return -ENOMEM; } memset(*pp, 0, alloc_size); (*pp)->data.pcc_cfg_data.cfg_payload = (void *)((unsigned long)(*pp) + sizeof(struct msmfb_mdp_pp)); break; case mdp_op_gamut_cfg: alloc_size += __pp_compat_size_gamut(); *pp = compat_alloc_user_space(alloc_size); if (NULL == *pp) { pr_err("alloc from user size %d for pcc fail\n", alloc_size); return -ENOMEM; } memset(*pp, 0, alloc_size); (*pp)->data.gamut_cfg_data.cfg_payload = (void *)((unsigned long)(*pp) + sizeof(struct msmfb_mdp_pp)); break; case mdp_op_pa_v2_cfg: alloc_size += __pp_compat_size_pa(); *pp = compat_alloc_user_space(alloc_size); if (NULL == *pp) { pr_err("alloc from user size %d for pcc fail\n", alloc_size); return -ENOMEM; } memset(*pp, 0, alloc_size); (*pp)->data.pa_v2_cfg_data.cfg_payload = (void *)((unsigned long)(*pp) + sizeof(struct msmfb_mdp_pp)); break; default: *pp = compat_alloc_user_space(alloc_size); if (NULL == *pp) return -ENOMEM; memset(*pp, 0, alloc_size); break; } return 0; } static int mdss_compat_pp_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg, struct file *file) { uint32_t op; int ret = 0; struct msmfb_mdp_pp32 __user *pp32; struct msmfb_mdp_pp __user *pp; pp32 = compat_ptr(arg); if (copy_from_user(&op, &pp32->op, sizeof(uint32_t))) return -EFAULT; ret = __pp_compat_alloc(pp32, &pp, op); if (ret) return ret; if (copy_in_user(&pp->op, &pp32->op, sizeof(uint32_t))) return -EFAULT; switch (op) { case mdp_op_pcc_cfg: ret = __from_user_pcc_cfg_data( compat_ptr((uintptr_t)&pp32->data.pcc_cfg_data), &pp->data.pcc_cfg_data); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); if (ret) goto pp_compat_exit; ret = __to_user_pcc_cfg_data( compat_ptr((uintptr_t)&pp32->data.pcc_cfg_data), &pp->data.pcc_cfg_data); break; case mdp_op_csc_cfg: ret = __from_user_csc_cfg_data( compat_ptr((uintptr_t)&pp32->data.csc_cfg_data), &pp->data.csc_cfg_data); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); if (ret) goto pp_compat_exit; ret = __to_user_csc_cfg_data( compat_ptr((uintptr_t)&pp32->data.csc_cfg_data), &pp->data.csc_cfg_data); break; case mdp_op_lut_cfg: ret = __from_user_lut_cfg_data( compat_ptr((uintptr_t)&pp32->data.lut_cfg_data), &pp->data.lut_cfg_data); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); if (ret) goto pp_compat_exit; ret = __to_user_lut_cfg_data( compat_ptr((uintptr_t)&pp32->data.lut_cfg_data), &pp->data.lut_cfg_data); break; case mdp_op_qseed_cfg: ret = __from_user_qseed_cfg_data( compat_ptr((uintptr_t)&pp32->data.qseed_cfg_data), &pp->data.qseed_cfg_data); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); if (ret) goto pp_compat_exit; ret = __to_user_qseed_cfg_data( compat_ptr((uintptr_t)&pp32->data.qseed_cfg_data), &pp->data.qseed_cfg_data); break; case mdp_bl_scale_cfg: ret = __from_user_bl_scale_data( compat_ptr((uintptr_t)&pp32->data.bl_scale_data), &pp->data.bl_scale_data); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); break; case mdp_op_pa_cfg: ret = __from_user_pa_cfg_data( compat_ptr((uintptr_t)&pp32->data.pa_cfg_data), &pp->data.pa_cfg_data); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); if (ret) goto pp_compat_exit; ret = __to_user_pa_cfg_data( compat_ptr((uintptr_t)&pp32->data.pa_cfg_data), &pp->data.pa_cfg_data); break; case mdp_op_pa_v2_cfg: ret = __from_user_pa_v2_cfg_data( compat_ptr((uintptr_t)&pp32->data.pa_v2_cfg_data), &pp->data.pa_v2_cfg_data); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); if (ret) goto pp_compat_exit; ret = __to_user_pa_v2_cfg_data( compat_ptr((uintptr_t)&pp32->data.pa_v2_cfg_data), &pp->data.pa_v2_cfg_data); break; case mdp_op_dither_cfg: ret = __from_user_dither_cfg_data( compat_ptr((uintptr_t)&pp32->data.dither_cfg_data), &pp->data.dither_cfg_data); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); if (ret) goto pp_compat_exit; ret = __to_user_dither_cfg_data( compat_ptr((uintptr_t)&pp32->data.dither_cfg_data), &pp->data.dither_cfg_data); break; case mdp_op_gamut_cfg: ret = __from_user_gamut_cfg_data( compat_ptr((uintptr_t)&pp32->data.gamut_cfg_data), &pp->data.gamut_cfg_data); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); if (ret) goto pp_compat_exit; ret = __to_user_gamut_cfg_data( compat_ptr((uintptr_t)&pp32->data.gamut_cfg_data), &pp->data.gamut_cfg_data); break; case mdp_op_calib_cfg: ret = __from_user_calib_config_data( compat_ptr((uintptr_t)&pp32->data.calib_cfg), &pp->data.calib_cfg); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); if (ret) goto pp_compat_exit; ret = __to_user_calib_config_data( compat_ptr((uintptr_t)&pp32->data.calib_cfg), &pp->data.calib_cfg); break; case mdp_op_ad_cfg: ret = __from_user_ad_init_cfg( compat_ptr((uintptr_t)&pp32->data.ad_init_cfg), &pp->data.ad_init_cfg); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); break; case mdp_op_ad_input: ret = __from_user_ad_input( compat_ptr((uintptr_t)&pp32->data.ad_input), &pp->data.ad_input); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); if (ret) goto pp_compat_exit; ret = __to_user_ad_input( compat_ptr((uintptr_t)&pp32->data.ad_input), &pp->data.ad_input); break; case mdp_op_calib_mode: ret = __from_user_calib_cfg( compat_ptr((uintptr_t)&pp32->data.mdss_calib_cfg), &pp->data.mdss_calib_cfg); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); break; case mdp_op_calib_buffer: ret = __from_user_calib_config_buffer( compat_ptr((uintptr_t)&pp32->data.calib_buffer), &pp->data.calib_buffer); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); if (ret) goto pp_compat_exit; ret = __to_user_calib_config_buffer( compat_ptr((uintptr_t)&pp32->data.calib_buffer), &pp->data.calib_buffer); break; case mdp_op_calib_dcm_state: ret = __from_user_calib_dcm_state( compat_ptr((uintptr_t)&pp32->data.calib_dcm), &pp->data.calib_dcm); if (ret) goto pp_compat_exit; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) pp, file); break; default: break; } pp_compat_exit: return ret; } static int __from_user_pp_params(struct mdp_overlay_pp_params32 *ppp32, struct mdp_overlay_pp_params *ppp) { int ret = 0; if (copy_in_user(&ppp->config_ops, &ppp32->config_ops, sizeof(uint32_t))) return -EFAULT; ret = __from_user_csc_cfg( compat_ptr((uintptr_t)&ppp32->csc_cfg), &ppp->csc_cfg); if (ret) return ret; ret = __from_user_qseed_cfg( compat_ptr((uintptr_t)&ppp32->qseed_cfg[0]), &ppp->qseed_cfg[0]); if (ret) return ret; ret = __from_user_qseed_cfg( compat_ptr((uintptr_t)&ppp32->qseed_cfg[1]), &ppp->qseed_cfg[1]); if (ret) return ret; ret = __from_user_pa_cfg( compat_ptr((uintptr_t)&ppp32->pa_cfg), &ppp->pa_cfg); if (ret) return ret; ret = __from_user_igc_lut_data( compat_ptr((uintptr_t)&ppp32->igc_cfg), &ppp->igc_cfg); if (ret) return ret; ret = __from_user_sharp_cfg( compat_ptr((uintptr_t)&ppp32->sharp_cfg), &ppp->sharp_cfg); if (ret) return ret; ret = __from_user_histogram_cfg( compat_ptr((uintptr_t)&ppp32->hist_cfg), &ppp->hist_cfg); if (ret) return ret; ret = __from_user_hist_lut_data( compat_ptr((uintptr_t)&ppp32->hist_lut_cfg), &ppp->hist_lut_cfg); if (ret) return ret; ret = __from_user_pa_v2_data( compat_ptr((uintptr_t)&ppp32->pa_v2_cfg), &ppp->pa_v2_cfg); return ret; } static int __to_user_pp_params(struct mdp_overlay_pp_params *ppp, struct mdp_overlay_pp_params32 *ppp32) { int ret = 0; if (copy_in_user(&ppp32->config_ops, &ppp->config_ops, sizeof(uint32_t))) return -EFAULT; ret = __to_user_csc_cfg( compat_ptr((uintptr_t)&ppp32->csc_cfg), &ppp->csc_cfg); if (ret) return ret; ret = __to_user_qseed_cfg( compat_ptr((uintptr_t)&ppp32->qseed_cfg[0]), &ppp->qseed_cfg[0]); if (ret) return ret; ret = __to_user_qseed_cfg( compat_ptr((uintptr_t)&ppp32->qseed_cfg[1]), &ppp->qseed_cfg[1]); if (ret) return ret; ret = __to_user_pa_cfg( compat_ptr((uintptr_t)&ppp32->pa_cfg), &ppp->pa_cfg); if (ret) return ret; ret = __to_user_igc_lut_data( compat_ptr((uintptr_t)&ppp32->igc_cfg), &ppp->igc_cfg); if (ret) return ret; ret = __to_user_sharp_cfg( compat_ptr((uintptr_t)&ppp32->sharp_cfg), &ppp->sharp_cfg); if (ret) return ret; ret = __to_user_histogram_cfg( compat_ptr((uintptr_t)&ppp32->hist_cfg), &ppp->hist_cfg); if (ret) return ret; ret = __to_user_hist_lut_data( compat_ptr((uintptr_t)&ppp32->hist_lut_cfg), &ppp->hist_lut_cfg); if (ret) return ret; ret = __to_user_pa_v2_data( compat_ptr((uintptr_t)&ppp32->pa_v2_cfg), &ppp->pa_v2_cfg); return ret; } static int __from_user_hist_start_req( struct mdp_histogram_start_req32 __user *hist_req32, struct mdp_histogram_start_req __user *hist_req) { if (copy_in_user(&hist_req->block, &hist_req32->block, sizeof(uint32_t)) || copy_in_user(&hist_req->frame_cnt, &hist_req32->frame_cnt, sizeof(uint8_t)) || copy_in_user(&hist_req->bit_mask, &hist_req32->bit_mask, sizeof(uint8_t)) || copy_in_user(&hist_req->num_bins, &hist_req32->num_bins, sizeof(uint16_t))) return -EFAULT; return 0; } static int __from_user_hist_data( struct mdp_histogram_data32 __user *hist_data32, struct mdp_histogram_data __user *hist_data) { uint32_t data; if (copy_in_user(&hist_data->block, &hist_data32->block, sizeof(uint32_t)) || copy_in_user(&hist_data->bin_cnt, &hist_data32->bin_cnt, sizeof(uint32_t))) return -EFAULT; if (get_user(data, &hist_data32->c0) || put_user(compat_ptr(data), &hist_data->c0) || get_user(data, &hist_data32->c1) || put_user(compat_ptr(data), &hist_data->c1) || get_user(data, &hist_data32->c2) || put_user(compat_ptr(data), &hist_data->c2) || get_user(data, &hist_data32->extra_info) || put_user(compat_ptr(data), &hist_data->extra_info)) return -EFAULT; return 0; } static int __to_user_hist_data( struct mdp_histogram_data32 __user *hist_data32, struct mdp_histogram_data __user *hist_data) { unsigned long data; if (copy_in_user(&hist_data32->block, &hist_data->block, sizeof(uint32_t)) || copy_in_user(&hist_data32->bin_cnt, &hist_data->bin_cnt, sizeof(uint32_t))) return -EFAULT; if (get_user(data, (unsigned long *) &hist_data->c0) || put_user((compat_caddr_t) data, &hist_data32->c0) || get_user(data, (unsigned long *) &hist_data->c1) || put_user((compat_caddr_t) data, &hist_data32->c1) || get_user(data, (unsigned long *) &hist_data->c2) || put_user((compat_caddr_t) data, &hist_data32->c2) || get_user(data, (unsigned long *) &hist_data->extra_info) || put_user((compat_caddr_t) data, &hist_data32->extra_info)) return -EFAULT; return 0; } static int mdss_histo_compat_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg, struct file *file) { struct mdp_histogram_data __user *hist; struct mdp_histogram_data32 __user *hist32; struct mdp_histogram_start_req __user *hist_req; struct mdp_histogram_start_req32 __user *hist_req32; int ret = 0; switch (cmd) { case MSMFB_HISTOGRAM_START: hist_req32 = compat_ptr(arg); hist_req = compat_alloc_user_space( sizeof(struct mdp_histogram_start_req)); if (!hist_req) { pr_err("%s:%u: compat alloc error [%zu] bytes\n", __func__, __LINE__, sizeof(struct mdp_histogram_start_req)); return -EINVAL; } memset(hist_req, 0, sizeof(struct mdp_histogram_start_req)); ret = __from_user_hist_start_req(hist_req32, hist_req); if (ret) goto histo_compat_err; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) hist_req, file); break; case MSMFB_HISTOGRAM_STOP: ret = mdss_fb_do_ioctl(info, cmd, arg, file); break; case MSMFB_HISTOGRAM: hist32 = compat_ptr(arg); hist = compat_alloc_user_space( sizeof(struct mdp_histogram_data)); if (!hist) { pr_err("%s:%u: compat alloc error [%zu] bytes\n", __func__, __LINE__, sizeof(struct mdp_histogram_data)); return -EINVAL; } memset(hist, 0, sizeof(struct mdp_histogram_data)); ret = __from_user_hist_data(hist32, hist); if (ret) goto histo_compat_err; ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) hist, file); if (ret) goto histo_compat_err; ret = __to_user_hist_data(hist32, hist); break; default: break; } histo_compat_err: return ret; } static int __copy_layer_pp_info_qseed_params( struct mdp_overlay_pp_params *pp_info, struct mdp_overlay_pp_params32 *pp_info32) { pp_info->qseed_cfg[0].table_num = pp_info32->qseed_cfg[0].table_num; pp_info->qseed_cfg[0].ops = pp_info32->qseed_cfg[0].ops; pp_info->qseed_cfg[0].len = pp_info32->qseed_cfg[0].len; pp_info->qseed_cfg[0].data = compat_ptr(pp_info32->qseed_cfg[0].data); pp_info->qseed_cfg[1].table_num = pp_info32->qseed_cfg[1].table_num; pp_info->qseed_cfg[1].ops = pp_info32->qseed_cfg[1].ops; pp_info->qseed_cfg[1].len = pp_info32->qseed_cfg[1].len; pp_info->qseed_cfg[1].data = compat_ptr(pp_info32->qseed_cfg[1].data); return 0; } static int __copy_layer_igc_lut_data_v1_7( struct mdp_igc_lut_data_v1_7 *cfg_payload, struct mdp_igc_lut_data_v1_7_32 __user *cfg_payload32) { struct mdp_igc_lut_data_v1_7_32 local_cfg_payload32; int ret = 0; ret = copy_from_user(&local_cfg_payload32, cfg_payload32, sizeof(struct mdp_igc_lut_data_v1_7_32)); if (ret) { pr_err("copy from user failed, IGC cfg payload = %p\n", cfg_payload32); ret = -EFAULT; goto exit; } cfg_payload->table_fmt = local_cfg_payload32.table_fmt; cfg_payload->len = local_cfg_payload32.len; cfg_payload->c0_c1_data = compat_ptr(local_cfg_payload32.c0_c1_data); cfg_payload->c2_data = compat_ptr(local_cfg_payload32.c2_data); exit: return ret; } static int __copy_layer_pp_info_igc_params( struct mdp_overlay_pp_params *pp_info, struct mdp_overlay_pp_params32 *pp_info32) { void *cfg_payload = NULL; uint32_t payload_size = 0; int ret = 0; pp_info->igc_cfg.block = pp_info32->igc_cfg.block; pp_info->igc_cfg.version = pp_info32->igc_cfg.version; pp_info->igc_cfg.ops = pp_info32->igc_cfg.ops; if (pp_info->igc_cfg.version != 0) { payload_size = __pp_compat_size_igc(); cfg_payload = kmalloc(payload_size, GFP_KERNEL); if (!cfg_payload) { ret = -ENOMEM; goto exit; } } switch (pp_info->igc_cfg.version) { case mdp_igc_v1_7: ret = __copy_layer_igc_lut_data_v1_7(cfg_payload, compat_ptr(pp_info32->igc_cfg.cfg_payload)); if (ret) { pr_err("compat copy of IGC cfg payload failed, ret %d\n", ret); kfree(cfg_payload); cfg_payload = NULL; goto exit; } break; default: pr_debug("No version set, fallback to legacy IGC version\n"); pp_info->igc_cfg.len = pp_info32->igc_cfg.len; pp_info->igc_cfg.c0_c1_data = compat_ptr(pp_info32->igc_cfg.c0_c1_data); pp_info->igc_cfg.c2_data = compat_ptr(pp_info32->igc_cfg.c2_data); cfg_payload = NULL; break; } exit: pp_info->igc_cfg.cfg_payload = cfg_payload; return ret; } static int __copy_layer_hist_lut_data_v1_7( struct mdp_hist_lut_data_v1_7 *cfg_payload, struct mdp_hist_lut_data_v1_7_32 __user *cfg_payload32) { struct mdp_hist_lut_data_v1_7_32 local_cfg_payload32; int ret = 0; ret = copy_from_user(&local_cfg_payload32, cfg_payload32, sizeof(struct mdp_hist_lut_data_v1_7_32)); if (ret) { pr_err("copy from user failed, hist lut cfg_payload = %p\n", cfg_payload32); ret = -EFAULT; goto exit; } cfg_payload->len = local_cfg_payload32.len; cfg_payload->data = compat_ptr(local_cfg_payload32.data); exit: return ret; } static int __copy_layer_pp_info_hist_lut_params( struct mdp_overlay_pp_params *pp_info, struct mdp_overlay_pp_params32 *pp_info32) { void *cfg_payload = NULL; uint32_t payload_size = 0; int ret = 0; pp_info->hist_lut_cfg.block = pp_info32->hist_lut_cfg.block; pp_info->hist_lut_cfg.version = pp_info32->hist_lut_cfg.version; pp_info->hist_lut_cfg.ops = pp_info32->hist_lut_cfg.ops; pp_info->hist_lut_cfg.hist_lut_first = pp_info32->hist_lut_cfg.hist_lut_first; if (pp_info->hist_lut_cfg.version != 0) { payload_size = __pp_compat_size_hist_lut(); cfg_payload = kmalloc(payload_size, GFP_KERNEL); if (!cfg_payload) { ret = -ENOMEM; goto exit; } } switch (pp_info->hist_lut_cfg.version) { case mdp_hist_lut_v1_7: ret = __copy_layer_hist_lut_data_v1_7(cfg_payload, compat_ptr(pp_info32->hist_lut_cfg.cfg_payload)); if (ret) { pr_err("compat copy of Hist LUT cfg payload failed, ret %d\n", ret); kfree(cfg_payload); cfg_payload = NULL; goto exit; } break; default: pr_debug("version invalid, fallback to legacy\n"); pp_info->hist_lut_cfg.len = pp_info32->hist_lut_cfg.len; pp_info->hist_lut_cfg.data = compat_ptr(pp_info32->hist_lut_cfg.data); cfg_payload = NULL; break; } exit: pp_info->hist_lut_cfg.cfg_payload = cfg_payload; return ret; } static int __copy_layer_pa_data_v1_7( struct mdp_pa_data_v1_7 *cfg_payload, struct mdp_pa_data_v1_7_32 __user *cfg_payload32) { struct mdp_pa_data_v1_7_32 local_cfg_payload32; int ret = 0; ret = copy_from_user(&local_cfg_payload32, cfg_payload32, sizeof(struct mdp_pa_data_v1_7_32)); if (ret) { pr_err("copy from user failed, pa cfg_payload = %p\n", cfg_payload32); ret = -EFAULT; goto exit; } cfg_payload->mode = local_cfg_payload32.mode; cfg_payload->global_hue_adj = local_cfg_payload32.global_hue_adj; cfg_payload->global_sat_adj = local_cfg_payload32.global_sat_adj; cfg_payload->global_val_adj = local_cfg_payload32.global_val_adj; cfg_payload->global_cont_adj = local_cfg_payload32.global_cont_adj; memcpy(&cfg_payload->skin_cfg, &local_cfg_payload32.skin_cfg, sizeof(struct mdp_pa_mem_col_data_v1_7)); memcpy(&cfg_payload->sky_cfg, &local_cfg_payload32.sky_cfg, sizeof(struct mdp_pa_mem_col_data_v1_7)); memcpy(&cfg_payload->fol_cfg, &local_cfg_payload32.fol_cfg, sizeof(struct mdp_pa_mem_col_data_v1_7)); cfg_payload->six_zone_thresh = local_cfg_payload32.six_zone_thresh; cfg_payload->six_zone_adj_p0 = local_cfg_payload32.six_zone_adj_p0; cfg_payload->six_zone_adj_p1 = local_cfg_payload32.six_zone_adj_p1; cfg_payload->six_zone_sat_hold = local_cfg_payload32.six_zone_sat_hold; cfg_payload->six_zone_val_hold = local_cfg_payload32.six_zone_val_hold; cfg_payload->six_zone_len = local_cfg_payload32.six_zone_len; cfg_payload->six_zone_curve_p0 = compat_ptr(local_cfg_payload32.six_zone_curve_p0); cfg_payload->six_zone_curve_p1 = compat_ptr(local_cfg_payload32.six_zone_curve_p1); exit: return ret; } static int __copy_layer_pp_info_pa_v2_params( struct mdp_overlay_pp_params *pp_info, struct mdp_overlay_pp_params32 *pp_info32) { void *cfg_payload = NULL; uint32_t payload_size = 0; int ret = 0; pp_info->pa_v2_cfg_data.block = pp_info32->pa_v2_cfg_data.block; pp_info->pa_v2_cfg_data.version = pp_info32->pa_v2_cfg_data.version; pp_info->pa_v2_cfg_data.flags = pp_info32->pa_v2_cfg_data.flags; if (pp_info->pa_v2_cfg_data.version != 0) { payload_size = __pp_compat_size_pa(); cfg_payload = kmalloc(payload_size, GFP_KERNEL); if (!cfg_payload) { ret = -ENOMEM; goto exit; } } switch (pp_info->pa_v2_cfg_data.version) { case mdp_pa_v1_7: ret = __copy_layer_pa_data_v1_7(cfg_payload, compat_ptr(pp_info32->pa_v2_cfg_data.cfg_payload)); if (ret) { pr_err("compat copy of PA cfg payload failed, ret %d\n", ret); kfree(cfg_payload); cfg_payload = NULL; goto exit; } break; default: pr_debug("version invalid\n"); cfg_payload = NULL; break; } exit: pp_info->pa_v2_cfg_data.cfg_payload = cfg_payload; return ret; } static int __copy_layer_pp_info_legacy_pa_v2_params( struct mdp_overlay_pp_params *pp_info, struct mdp_overlay_pp_params32 *pp_info32) { pp_info->pa_v2_cfg.global_hue_adj = pp_info32->pa_v2_cfg.global_hue_adj; pp_info->pa_v2_cfg.global_sat_adj = pp_info32->pa_v2_cfg.global_sat_adj; pp_info->pa_v2_cfg.global_val_adj = pp_info32->pa_v2_cfg.global_val_adj; pp_info->pa_v2_cfg.global_cont_adj = pp_info32->pa_v2_cfg.global_cont_adj; memcpy(&pp_info->pa_v2_cfg.skin_cfg, &pp_info32->pa_v2_cfg.skin_cfg, sizeof(struct mdp_pa_mem_col_cfg)); memcpy(&pp_info->pa_v2_cfg.sky_cfg, &pp_info32->pa_v2_cfg.sky_cfg, sizeof(struct mdp_pa_mem_col_cfg)); memcpy(&pp_info->pa_v2_cfg.fol_cfg, &pp_info32->pa_v2_cfg.fol_cfg, sizeof(struct mdp_pa_mem_col_cfg)); pp_info->pa_v2_cfg.six_zone_thresh = pp_info32->pa_v2_cfg.six_zone_thresh; pp_info->pa_v2_cfg.six_zone_len = pp_info32->pa_v2_cfg.six_zone_len; pp_info->pa_v2_cfg.six_zone_curve_p0 = compat_ptr(pp_info32->pa_v2_cfg.six_zone_curve_p0); pp_info->pa_v2_cfg.six_zone_curve_p1 = compat_ptr(pp_info32->pa_v2_cfg.six_zone_curve_p1); return 0; } static int __copy_layer_pp_info_pcc_params( struct mdp_overlay_pp_params *pp_info, struct mdp_overlay_pp_params32 *pp_info32) { void *cfg_payload = NULL; uint32_t payload_size = 0; int ret = 0; pp_info->pcc_cfg_data.block = pp_info32->pcc_cfg_data.block; pp_info->pcc_cfg_data.version = pp_info32->pcc_cfg_data.version; pp_info->pcc_cfg_data.ops = pp_info32->pcc_cfg_data.ops; if (pp_info->pcc_cfg_data.version != 0) { payload_size = __pp_compat_size_pcc(); cfg_payload = kmalloc(payload_size, GFP_KERNEL); if (!cfg_payload) { ret = -ENOMEM; goto exit; } } switch (pp_info->pcc_cfg_data.version) { case mdp_pcc_v1_7: ret = copy_from_user(cfg_payload, compat_ptr(pp_info32->pcc_cfg_data.cfg_payload), sizeof(struct mdp_pcc_data_v1_7)); if (ret) { pr_err("compat copy of PCC cfg payload failed, ptr %p\n", compat_ptr( pp_info32->pcc_cfg_data.cfg_payload)); ret = -EFAULT; kfree(cfg_payload); cfg_payload = NULL; goto exit; } break; default: pr_debug("version invalid, fallback to legacy\n"); cfg_payload = NULL; break; } exit: pp_info->pcc_cfg_data.cfg_payload = cfg_payload; return ret; } static int __copy_layer_pp_info_params(struct mdp_input_layer *layer, struct mdp_input_layer32 *layer32) { struct mdp_overlay_pp_params *pp_info; struct mdp_overlay_pp_params32 pp_info32; int ret = 0; if (!(layer->flags & MDP_LAYER_PP)) return 0; ret = copy_from_user(&pp_info32, compat_ptr(layer32->pp_info), sizeof(struct mdp_overlay_pp_params32)); if (ret) { pr_err("pp info copy from user failed, pp_info %p\n", compat_ptr(layer32->pp_info)); ret = -EFAULT; goto exit; } pp_info = kmalloc(sizeof(struct mdp_overlay_pp_params), GFP_KERNEL); if (!pp_info) { ret = -ENOMEM; goto exit; } memset(pp_info, 0, sizeof(struct mdp_overlay_pp_params)); pp_info->config_ops = pp_info32.config_ops; memcpy(&pp_info->csc_cfg, &pp_info32.csc_cfg, sizeof(struct mdp_csc_cfg)); memcpy(&pp_info->sharp_cfg, &pp_info32.sharp_cfg, sizeof(struct mdp_sharp_cfg)); memcpy(&pp_info->hist_cfg, &pp_info32.hist_cfg, sizeof(struct mdp_histogram_cfg)); memcpy(&pp_info->pa_cfg, &pp_info32.pa_cfg, sizeof(struct mdp_pa_cfg)); ret = __copy_layer_pp_info_qseed_params(pp_info, &pp_info32); if (ret) { pr_err("compat copy pp_info QSEED params failed, ret %d\n", ret); goto exit_pp_info; } ret = __copy_layer_pp_info_legacy_pa_v2_params(pp_info, &pp_info32); if (ret) { pr_err("compat copy pp_info Legacy PAv2 params failed, ret %d\n", ret); goto exit_pp_info; } ret = __copy_layer_pp_info_igc_params(pp_info, &pp_info32); if (ret) { pr_err("compat copy pp_info IGC params failed, ret %d\n", ret); goto exit_pp_info; } ret = __copy_layer_pp_info_hist_lut_params(pp_info, &pp_info32); if (ret) { pr_err("compat copy pp_info Hist LUT params failed, ret %d\n", ret); goto exit_igc; } ret = __copy_layer_pp_info_pa_v2_params(pp_info, &pp_info32); if (ret) { pr_err("compat copy pp_info PAv2 params failed, ret %d\n", ret); goto exit_hist_lut; } ret = __copy_layer_pp_info_pcc_params(pp_info, &pp_info32); if (ret) { pr_err("compat copy pp_info PCC params failed, ret %d\n", ret); goto exit_pa; } layer->pp_info = pp_info; return ret; exit_pa: kfree(pp_info->pa_v2_cfg_data.cfg_payload); exit_hist_lut: kfree(pp_info->hist_lut_cfg.cfg_payload); exit_igc: kfree(pp_info->igc_cfg.cfg_payload); exit_pp_info: kfree(pp_info); exit: return ret; } static int __to_user_mdp_overlay(struct mdp_overlay32 __user *ov32, struct mdp_overlay __user *ov) { int ret = 0; ret = copy_in_user(&ov32->src, &ov->src, sizeof(ov32->src)) || copy_in_user(&ov32->src_rect, &ov->src_rect, sizeof(ov32->src_rect)) || copy_in_user(&ov32->dst_rect, &ov->dst_rect, sizeof(ov32->dst_rect)); if (ret) return -EFAULT; ret |= put_user(ov->z_order, &ov32->z_order); ret |= put_user(ov->is_fg, &ov32->is_fg); ret |= put_user(ov->alpha, &ov32->alpha); ret |= put_user(ov->blend_op, &ov32->blend_op); ret |= put_user(ov->transp_mask, &ov32->transp_mask); ret |= put_user(ov->flags, &ov32->flags); ret |= put_user(ov->id, &ov32->id); ret |= put_user(ov->priority, &ov32->priority); if (ret) return -EFAULT; ret = copy_in_user(&ov32->user_data, &ov->user_data, sizeof(ov32->user_data)); if (ret) return -EFAULT; ret |= put_user(ov->horz_deci, &ov32->horz_deci); ret |= put_user(ov->vert_deci, &ov32->vert_deci); if (ret) return -EFAULT; ret = __to_user_pp_params( &ov->overlay_pp_cfg, compat_ptr((uintptr_t) &ov32->overlay_pp_cfg)); if (ret) return -EFAULT; ret = copy_in_user(&ov32->scale, &ov->scale, sizeof(struct mdp_scale_data)); if (ret) return -EFAULT; ret = put_user(ov->frame_rate, &ov32->frame_rate); if (ret) return -EFAULT; return 0; } static int __from_user_mdp_overlay(struct mdp_overlay *ov, struct mdp_overlay32 __user *ov32) { __u32 data; if (copy_in_user(&ov->src, &ov32->src, sizeof(ov32->src)) || copy_in_user(&ov->src_rect, &ov32->src_rect, sizeof(ov32->src_rect)) || copy_in_user(&ov->dst_rect, &ov32->dst_rect, sizeof(ov32->dst_rect))) return -EFAULT; if (get_user(data, &ov32->z_order) || put_user(data, &ov->z_order) || get_user(data, &ov32->is_fg) || put_user(data, &ov->is_fg) || get_user(data, &ov32->alpha) || put_user(data, &ov->alpha) || get_user(data, &ov32->blend_op) || put_user(data, &ov->blend_op) || get_user(data, &ov32->transp_mask) || put_user(data, &ov->transp_mask) || get_user(data, &ov32->flags) || put_user(data, &ov->flags) || get_user(data, &ov32->pipe_type) || put_user(data, &ov->pipe_type) || get_user(data, &ov32->id) || put_user(data, &ov->id) || get_user(data, &ov32->priority) || put_user(data, &ov->priority)) return -EFAULT; if (copy_in_user(&ov->user_data, &ov32->user_data, sizeof(ov32->user_data))) return -EFAULT; if (get_user(data, &ov32->horz_deci) || put_user(data, &ov->horz_deci) || get_user(data, &ov32->vert_deci) || put_user(data, &ov->vert_deci)) return -EFAULT; if (__from_user_pp_params( compat_ptr((uintptr_t) &ov32->overlay_pp_cfg), &ov->overlay_pp_cfg)) return -EFAULT; if (copy_in_user(&ov->scale, &ov32->scale, sizeof(struct mdp_scale_data))) return -EFAULT; if (get_user(data, &ov32->frame_rate) || put_user(data, &ov->frame_rate)) return -EFAULT; return 0; } static int __from_user_mdp_overlaylist(struct mdp_overlay_list *ovlist, struct mdp_overlay_list32 *ovlist32, struct mdp_overlay **to_list_head) { __u32 i, ret; unsigned long data, from_list_head; struct mdp_overlay32 *iter; if (!to_list_head || !ovlist32 || !ovlist) { pr_err("%s:%u: null error\n", __func__, __LINE__); return -EINVAL; } if (copy_in_user(&ovlist->num_overlays, &ovlist32->num_overlays, sizeof(ovlist32->num_overlays))) return -EFAULT; if (copy_in_user(&ovlist->flags, &ovlist32->flags, sizeof(ovlist32->flags))) return -EFAULT; if (copy_in_user(&ovlist->processed_overlays, &ovlist32->processed_overlays, sizeof(ovlist32->processed_overlays))) return -EFAULT; if (get_user(data, &ovlist32->overlay_list)) { ret = -EFAULT; goto validate_exit; } for (i = 0; i < ovlist32->num_overlays; i++) { if (get_user(from_list_head, (__u32 *)data + i)) { ret = -EFAULT; goto validate_exit; } iter = compat_ptr(from_list_head); if (__from_user_mdp_overlay(to_list_head[i], (struct mdp_overlay32 *)(iter))) { ret = -EFAULT; goto validate_exit; } } ovlist->overlay_list = to_list_head; return 0; validate_exit: pr_err("%s: %u: copy error\n", __func__, __LINE__); return -EFAULT; } static int __to_user_mdp_overlaylist(struct mdp_overlay_list32 *ovlist32, struct mdp_overlay_list *ovlist, struct mdp_overlay **l_ptr) { __u32 i, ret; unsigned long data, data1; struct mdp_overlay32 *temp; struct mdp_overlay *l = l_ptr[0]; if (copy_in_user(&ovlist32->num_overlays, &ovlist->num_overlays, sizeof(ovlist32->num_overlays))) return -EFAULT; if (get_user(data, &ovlist32->overlay_list)) { ret = -EFAULT; pr_err("%s:%u: err\n", __func__, __LINE__); goto validate_exit; } for (i = 0; i < ovlist32->num_overlays; i++) { if (get_user(data1, (__u32 *)data + i)) { ret = -EFAULT; goto validate_exit; } temp = compat_ptr(data1); if (__to_user_mdp_overlay( (struct mdp_overlay32 *) temp, l + i)) { ret = -EFAULT; goto validate_exit; } } if (copy_in_user(&ovlist32->flags, &ovlist->flags, sizeof(ovlist32->flags))) return -EFAULT; if (copy_in_user(&ovlist32->processed_overlays, &ovlist->processed_overlays, sizeof(ovlist32->processed_overlays))) return -EFAULT; return 0; validate_exit: pr_err("%s: %u: copy error\n", __func__, __LINE__); return -EFAULT; } void mdss_compat_align_list(void __user *total_mem_chunk, struct mdp_overlay __user **list_ptr, u32 num_ov) { int i = 0; struct mdp_overlay __user *contig_overlays; contig_overlays = total_mem_chunk + sizeof(struct mdp_overlay_list) + (num_ov * sizeof(struct mdp_overlay *)); for (i = 0; i < num_ov; i++) list_ptr[i] = contig_overlays + i; } static u32 __pp_sspp_size(void) { u32 size = 0; /* pick the largest of the revision when multiple revs are supported */ size = sizeof(struct mdp_igc_lut_data_v1_7); size += sizeof(struct mdp_pa_data_v1_7); size += sizeof(struct mdp_pcc_data_v1_7); size += sizeof(struct mdp_hist_lut_data_v1_7); return size; } static int __pp_sspp_set_offsets(struct mdp_overlay *ov) { if (!ov) { pr_err("invalid overlay pointer\n"); return -EFAULT; } ov->overlay_pp_cfg.igc_cfg.cfg_payload = (void *)((unsigned long)ov + sizeof(struct mdp_overlay)); ov->overlay_pp_cfg.pa_v2_cfg_data.cfg_payload = ov->overlay_pp_cfg.igc_cfg.cfg_payload + sizeof(struct mdp_igc_lut_data_v1_7); ov->overlay_pp_cfg.pcc_cfg_data.cfg_payload = ov->overlay_pp_cfg.pa_v2_cfg_data.cfg_payload + sizeof(struct mdp_pa_data_v1_7); ov->overlay_pp_cfg.hist_lut_cfg.cfg_payload = ov->overlay_pp_cfg.pcc_cfg_data.cfg_payload + sizeof(struct mdp_pcc_data_v1_7); return 0; } int mdss_compat_overlay_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg, struct file *file) { struct mdp_overlay *ov, **layers_head; struct mdp_overlay32 *ov32; struct mdp_overlay_list __user *ovlist; struct mdp_overlay_list32 __user *ovlist32; size_t layers_refs_sz, layers_sz, prepare_sz; void __user *total_mem_chunk; uint32_t num_overlays; uint32_t alloc_size = 0; int ret; if (!info || !info->par) return -EINVAL; switch (cmd) { case MSMFB_MDP_PP: ret = mdss_compat_pp_ioctl(info, cmd, arg, file); break; case MSMFB_HISTOGRAM_START: case MSMFB_HISTOGRAM_STOP: case MSMFB_HISTOGRAM: ret = mdss_histo_compat_ioctl(info, cmd, arg, file); break; case MSMFB_OVERLAY_GET: alloc_size += sizeof(*ov) + __pp_sspp_size(); ov = compat_alloc_user_space(alloc_size); if (!ov) { pr_err("%s:%u: compat alloc error [%zu] bytes\n", __func__, __LINE__, sizeof(*ov)); return -EINVAL; } ov32 = compat_ptr(arg); ret = __pp_sspp_set_offsets(ov); if (ret) { pr_err("setting the pp offsets failed ret %d\n", ret); return ret; } ret = __from_user_mdp_overlay(ov, ov32); if (ret) pr_err("%s: compat mdp overlay failed\n", __func__); else ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) ov, file); ret = __to_user_mdp_overlay(ov32, ov); break; case MSMFB_OVERLAY_SET: alloc_size += sizeof(*ov) + __pp_sspp_size(); ov = compat_alloc_user_space(alloc_size); if (!ov) { pr_err("%s:%u: compat alloc error [%zu] bytes\n", __func__, __LINE__, sizeof(*ov)); return -EINVAL; } ret = __pp_sspp_set_offsets(ov); if (ret) { pr_err("setting the pp offsets failed ret %d\n", ret); return ret; } ov32 = compat_ptr(arg); ret = __from_user_mdp_overlay(ov, ov32); if (ret) { pr_err("%s: compat mdp overlay failed\n", __func__); } else { ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) ov, file); ret = __to_user_mdp_overlay(ov32, ov); } break; case MSMFB_OVERLAY_PREPARE: ovlist32 = compat_ptr(arg); if (get_user(num_overlays, &ovlist32->num_overlays)) { pr_err("compat mdp prepare failed: invalid arg\n"); return -EFAULT; } if (num_overlays >= OVERLAY_MAX) { pr_err("%s: No: of overlays exceeds max\n", __func__); return -EINVAL; } layers_sz = num_overlays * sizeof(struct mdp_overlay); prepare_sz = sizeof(struct mdp_overlay_list); layers_refs_sz = num_overlays * sizeof(struct mdp_overlay *); total_mem_chunk = compat_alloc_user_space( prepare_sz + layers_refs_sz + layers_sz); if (!total_mem_chunk) { pr_err("%s:%u: compat alloc error [%zu] bytes\n", __func__, __LINE__, layers_refs_sz + layers_sz + prepare_sz); return -EINVAL; } layers_head = total_mem_chunk + prepare_sz; mdss_compat_align_list(total_mem_chunk, layers_head, num_overlays); ovlist = (struct mdp_overlay_list *)total_mem_chunk; ret = __from_user_mdp_overlaylist(ovlist, ovlist32, layers_head); if (ret) { pr_err("compat mdp overlaylist failed\n"); } else { ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) ovlist, file); if (!ret) ret = __to_user_mdp_overlaylist(ovlist32, ovlist, layers_head); } break; case MSMFB_OVERLAY_UNSET: case MSMFB_OVERLAY_PLAY: case MSMFB_OVERLAY_VSYNC_CTRL: case MSMFB_METADATA_SET: case MSMFB_METADATA_GET: default: pr_debug("%s: overlay ioctl cmd=[%u]\n", __func__, cmd); ret = mdss_fb_do_ioctl(info, cmd, (unsigned long) arg, file); break; } return ret; } /* * mdss_fb_compat_ioctl() - MDSS Framebuffer compat ioctl function * @info: pointer to framebuffer info * @cmd: ioctl command * @arg: argument to ioctl * * This function adds the compat translation layer for framebuffer * ioctls to allow 32-bit userspace call ioctls on the mdss * framebuffer device driven in 64-bit kernel. */ int mdss_fb_compat_ioctl(struct fb_info *info, unsigned int cmd, unsigned long arg, struct file *file) { int ret; if (!info || !info->par) return -EINVAL; cmd = __do_compat_ioctl_nr(cmd); switch (cmd) { case MSMFB_CURSOR: ret = mdss_fb_compat_cursor(info, cmd, arg, file); break; case MSMFB_SET_LUT: ret = mdss_fb_compat_set_lut(info, arg, file); break; case MSMFB_BUFFER_SYNC: ret = mdss_fb_compat_buf_sync(info, cmd, arg, file); break; case MSMFB_ATOMIC_COMMIT: ret = __compat_atomic_commit(info, cmd, arg, file); break; case MSMFB_ASYNC_POSITION_UPDATE: ret = __compat_async_position_update(info, cmd, arg); break; case MSMFB_MDP_PP: case MSMFB_HISTOGRAM_START: case MSMFB_HISTOGRAM_STOP: case MSMFB_HISTOGRAM: case MSMFB_OVERLAY_GET: case MSMFB_OVERLAY_SET: case MSMFB_OVERLAY_UNSET: case MSMFB_OVERLAY_PLAY: case MSMFB_OVERLAY_VSYNC_CTRL: case MSMFB_METADATA_SET: case MSMFB_METADATA_GET: case MSMFB_OVERLAY_PREPARE: ret = mdss_compat_overlay_ioctl(info, cmd, arg, file); break; case MSMFB_NOTIFY_UPDATE: case MSMFB_DISPLAY_COMMIT: default: ret = mdss_fb_do_ioctl(info, cmd, arg, file); break; } if (ret == -ENOSYS) pr_err("%s: unsupported ioctl\n", __func__); else if (ret) pr_debug("%s: ioctl err cmd=%u ret=%d\n", __func__, cmd, ret); return ret; } EXPORT_SYMBOL(mdss_fb_compat_ioctl);