2350 lines
60 KiB
C
2350 lines
60 KiB
C
/* Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 and
|
|
* only version 2 as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#define pr_fmt(fmt) "%s: " fmt, __func__
|
|
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/errno.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/major.h>
|
|
#include <linux/module.h>
|
|
#include <linux/uaccess.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/dma-buf.h>
|
|
|
|
#include "mdp3_ctrl.h"
|
|
#include "mdp3.h"
|
|
#include "mdp3_ppp.h"
|
|
|
|
#define VSYNC_EXPIRE_TICK 4
|
|
|
|
static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd);
|
|
static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx);
|
|
static int mdp3_histogram_stop(struct mdp3_session_data *session,
|
|
u32 block);
|
|
static int mdp3_ctrl_clk_enable(struct msm_fb_data_type *mfd, int enable);
|
|
static int mdp3_ctrl_vsync_enable(struct msm_fb_data_type *mfd, int enable);
|
|
static int mdp3_ctrl_get_intf_type(struct msm_fb_data_type *mfd);
|
|
static int mdp3_ctrl_lut_read(struct msm_fb_data_type *mfd,
|
|
struct mdp_rgb_lut_data *cfg);
|
|
static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd,
|
|
struct mdp_rgb_lut_data *cfg);
|
|
|
|
u32 mdp_lut_inverse16[MDP_LUT_SIZE] = {
|
|
0, 65536, 32768, 21845, 16384, 13107, 10923, 9362, 8192, 7282, 6554, 5958,
|
|
5461, 5041, 4681, 4369, 4096, 3855, 3641, 3449, 3277, 3121, 2979, 2849, 2731,
|
|
2621, 2521, 2427, 2341, 2260, 2185, 2114, 2048, 1986, 1928, 1872, 1820, 1771,
|
|
1725, 1680, 1638, 1598, 1560, 1524, 1489, 1456, 1425, 1394, 1365, 1337, 1311,
|
|
1285, 1260, 1237, 1214, 1192, 1170, 1150, 1130, 1111, 1092, 1074, 1057, 1040,
|
|
1024, 1008, 993, 978, 964, 950, 936, 923, 910, 898, 886, 874, 862, 851, 840,
|
|
830, 819, 809, 799, 790, 780, 771, 762, 753, 745, 736, 728, 720, 712, 705, 697,
|
|
690, 683, 676, 669, 662, 655, 649, 643, 636, 630, 624, 618, 612, 607, 601, 596,
|
|
590, 585, 580, 575, 570, 565, 560, 555, 551, 546, 542, 537, 533, 529, 524, 520,
|
|
516, 512, 508, 504, 500, 496, 493, 489, 485, 482, 478, 475, 471, 468, 465, 462,
|
|
458, 455, 452, 449, 446, 443, 440, 437, 434, 431, 428, 426, 423, 420, 417, 415,
|
|
412, 410, 407, 405, 402, 400, 397, 395, 392, 390, 388, 386, 383, 381, 379, 377,
|
|
374, 372, 370, 368, 366, 364, 362, 360, 358, 356, 354, 352, 350, 349, 347, 345,
|
|
343, 341, 340, 338, 336, 334, 333, 331, 329, 328, 326, 324, 323, 321, 320, 318,
|
|
317, 315, 314, 312, 311, 309, 308, 306, 305, 303, 302, 301, 299, 298, 297, 295,
|
|
294, 293, 291, 290, 289, 287, 286, 285, 284, 282, 281, 280, 279, 278, 277, 275,
|
|
274, 273, 272, 271, 270, 269, 267, 266, 265, 264, 263, 262, 261, 260, 259, 258,
|
|
257};
|
|
|
|
static void mdp3_bufq_init(struct mdp3_buffer_queue *bufq)
|
|
{
|
|
bufq->count = 0;
|
|
bufq->push_idx = 0;
|
|
bufq->pop_idx = 0;
|
|
}
|
|
|
|
static void mdp3_bufq_deinit(struct mdp3_buffer_queue *bufq)
|
|
{
|
|
int count = bufq->count;
|
|
|
|
if (!count)
|
|
return;
|
|
|
|
while (count-- && (bufq->pop_idx >= 0)) {
|
|
struct mdp3_img_data *data = &bufq->img_data[bufq->pop_idx];
|
|
bufq->pop_idx = (bufq->pop_idx + 1) % MDP3_MAX_BUF_QUEUE;
|
|
mdp3_put_img(data);
|
|
}
|
|
bufq->count = 0;
|
|
bufq->push_idx = 0;
|
|
bufq->pop_idx = 0;
|
|
}
|
|
|
|
static int mdp3_bufq_push(struct mdp3_buffer_queue *bufq,
|
|
struct mdp3_img_data *data)
|
|
{
|
|
if (bufq->count >= MDP3_MAX_BUF_QUEUE) {
|
|
pr_err("bufq full\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
bufq->img_data[bufq->push_idx] = *data;
|
|
bufq->push_idx = (bufq->push_idx + 1) % MDP3_MAX_BUF_QUEUE;
|
|
bufq->count++;
|
|
return 0;
|
|
}
|
|
|
|
static struct mdp3_img_data *mdp3_bufq_pop(struct mdp3_buffer_queue *bufq)
|
|
{
|
|
struct mdp3_img_data *data;
|
|
if (bufq->count == 0)
|
|
return NULL;
|
|
|
|
data = &bufq->img_data[bufq->pop_idx];
|
|
bufq->count--;
|
|
bufq->pop_idx = (bufq->pop_idx + 1) % MDP3_MAX_BUF_QUEUE;
|
|
return data;
|
|
}
|
|
|
|
static int mdp3_bufq_count(struct mdp3_buffer_queue *bufq)
|
|
{
|
|
return bufq->count;
|
|
}
|
|
|
|
void mdp3_ctrl_notifier_register(struct mdp3_session_data *ses,
|
|
struct notifier_block *notifier)
|
|
{
|
|
blocking_notifier_chain_register(&ses->notifier_head, notifier);
|
|
}
|
|
|
|
void mdp3_ctrl_notifier_unregister(struct mdp3_session_data *ses,
|
|
struct notifier_block *notifier)
|
|
{
|
|
blocking_notifier_chain_unregister(&ses->notifier_head, notifier);
|
|
}
|
|
|
|
int mdp3_ctrl_notify(struct mdp3_session_data *ses, int event)
|
|
{
|
|
return blocking_notifier_call_chain(&ses->notifier_head, event, ses);
|
|
}
|
|
|
|
static void mdp3_dispatch_dma_done(struct work_struct *work)
|
|
{
|
|
struct mdp3_session_data *session;
|
|
int cnt = 0;
|
|
|
|
pr_debug("%s\n", __func__);
|
|
session = container_of(work, struct mdp3_session_data,
|
|
dma_done_work);
|
|
if (!session)
|
|
return;
|
|
|
|
cnt = atomic_read(&session->dma_done_cnt);
|
|
|
|
while (cnt > 0) {
|
|
mdp3_ctrl_notify(session, MDP_NOTIFY_FRAME_DONE);
|
|
atomic_dec(&session->dma_done_cnt);
|
|
cnt--;
|
|
}
|
|
}
|
|
|
|
static void mdp3_dispatch_clk_off(struct work_struct *work)
|
|
{
|
|
struct mdp3_session_data *session;
|
|
|
|
pr_debug("%s\n", __func__);
|
|
session = container_of(work, struct mdp3_session_data,
|
|
clk_off_work);
|
|
if (!session)
|
|
return;
|
|
|
|
mutex_lock(&session->lock);
|
|
if (session->vsync_enabled ||
|
|
atomic_read(&session->vsync_countdown) != 0) {
|
|
mutex_unlock(&session->lock);
|
|
pr_debug("Ignoring clk shut down\n");
|
|
return;
|
|
}
|
|
|
|
mdp3_ctrl_vsync_enable(session->mfd, 0);
|
|
mdp3_ctrl_clk_enable(session->mfd, 0);
|
|
mutex_unlock(&session->lock);
|
|
}
|
|
|
|
void vsync_notify_handler(void *arg)
|
|
{
|
|
struct mdp3_session_data *session = (struct mdp3_session_data *)arg;
|
|
session->vsync_time = ktime_get();
|
|
sysfs_notify_dirent(session->vsync_event_sd);
|
|
}
|
|
|
|
void dma_done_notify_handler(void *arg)
|
|
{
|
|
struct mdp3_session_data *session = (struct mdp3_session_data *)arg;
|
|
atomic_inc(&session->dma_done_cnt);
|
|
schedule_work(&session->dma_done_work);
|
|
complete(&session->dma_completion);
|
|
}
|
|
|
|
void vsync_count_down(void *arg)
|
|
{
|
|
struct mdp3_session_data *session = (struct mdp3_session_data *)arg;
|
|
/* We are counting down to turn off clocks */
|
|
atomic_dec(&session->vsync_countdown);
|
|
if (atomic_read(&session->vsync_countdown) == 0)
|
|
schedule_work(&session->clk_off_work);
|
|
}
|
|
|
|
void mdp3_ctrl_reset_countdown(struct mdp3_session_data *session,
|
|
struct msm_fb_data_type *mfd)
|
|
{
|
|
if (mdp3_ctrl_get_intf_type(mfd) == MDP3_DMA_OUTPUT_SEL_DSI_CMD)
|
|
atomic_set(&session->vsync_countdown, VSYNC_EXPIRE_TICK);
|
|
}
|
|
|
|
static int mdp3_ctrl_vsync_enable(struct msm_fb_data_type *mfd, int enable)
|
|
{
|
|
struct mdp3_session_data *mdp3_session;
|
|
struct mdp3_notification vsync_client;
|
|
struct mdp3_notification *arg = NULL;
|
|
|
|
pr_debug("mdp3_ctrl_vsync_enable =%d\n", enable);
|
|
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
|
|
if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma ||
|
|
!mdp3_session->intf)
|
|
return -ENODEV;
|
|
|
|
if (!mdp3_session->status) {
|
|
pr_debug("fb%d is not on yet", mfd->index);
|
|
return -EINVAL;
|
|
}
|
|
if (enable) {
|
|
vsync_client.handler = vsync_notify_handler;
|
|
vsync_client.arg = mdp3_session;
|
|
arg = &vsync_client;
|
|
} else if (atomic_read(&mdp3_session->vsync_countdown)) {
|
|
/*
|
|
* Now that vsync is no longer needed we will
|
|
* shutdown dsi clocks as soon as cnt down == 0
|
|
* for cmd mode panels
|
|
*/
|
|
vsync_client.handler = vsync_count_down;
|
|
vsync_client.arg = mdp3_session;
|
|
arg = &vsync_client;
|
|
enable = 1;
|
|
}
|
|
|
|
mdp3_clk_enable(1, 0);
|
|
mdp3_session->dma->vsync_enable(mdp3_session->dma, arg);
|
|
mdp3_clk_enable(0, 0);
|
|
|
|
/*
|
|
* Need to fake vsync whenever dsi interface is not
|
|
* active or when dsi clocks are currently off
|
|
*/
|
|
if (enable && mdp3_session->status == 1
|
|
&& (mdp3_session->vsync_before_commit ||
|
|
!mdp3_session->intf->active)) {
|
|
mod_timer(&mdp3_session->vsync_timer,
|
|
jiffies + msecs_to_jiffies(mdp3_session->vsync_period));
|
|
} else if (enable && !mdp3_session->clk_on) {
|
|
mdp3_ctrl_reset_countdown(mdp3_session, mfd);
|
|
mdp3_ctrl_clk_enable(mfd, 1);
|
|
} else if (!enable) {
|
|
del_timer(&mdp3_session->vsync_timer);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
void mdp3_vsync_timer_func(unsigned long arg)
|
|
{
|
|
struct mdp3_session_data *session = (struct mdp3_session_data *)arg;
|
|
if (session->status == 1 && (session->vsync_before_commit ||
|
|
!session->intf->active)) {
|
|
pr_debug("mdp3_vsync_timer_func trigger\n");
|
|
vsync_notify_handler(session);
|
|
mod_timer(&session->vsync_timer,
|
|
jiffies + msecs_to_jiffies(session->vsync_period));
|
|
}
|
|
}
|
|
|
|
static int mdp3_ctrl_async_blit_req(struct msm_fb_data_type *mfd,
|
|
void __user *p)
|
|
{
|
|
struct mdp_async_blit_req_list req_list_header;
|
|
int rc, count;
|
|
void __user *p_req;
|
|
|
|
if (copy_from_user(&req_list_header, p, sizeof(req_list_header)))
|
|
return -EFAULT;
|
|
p_req = p + sizeof(req_list_header);
|
|
count = req_list_header.count;
|
|
if (count < 0 || count >= MAX_BLIT_REQ)
|
|
return -EINVAL;
|
|
rc = mdp3_ppp_parse_req(p_req, &req_list_header, 1);
|
|
if (!rc)
|
|
rc = copy_to_user(p, &req_list_header, sizeof(req_list_header));
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_ctrl_blit_req(struct msm_fb_data_type *mfd, void __user *p)
|
|
{
|
|
struct mdp_async_blit_req_list req_list_header;
|
|
int rc, count;
|
|
void __user *p_req;
|
|
|
|
if (copy_from_user(&(req_list_header.count), p,
|
|
sizeof(struct mdp_blit_req_list)))
|
|
return -EFAULT;
|
|
p_req = p + sizeof(struct mdp_blit_req_list);
|
|
count = req_list_header.count;
|
|
if (count < 0 || count >= MAX_BLIT_REQ)
|
|
return -EINVAL;
|
|
req_list_header.sync.acq_fen_fd_cnt = 0;
|
|
rc = mdp3_ppp_parse_req(p_req, &req_list_header, 0);
|
|
return rc;
|
|
}
|
|
|
|
static ssize_t mdp3_vsync_show_event(struct device *dev,
|
|
struct device_attribute *attr, char *buf)
|
|
{
|
|
struct fb_info *fbi = dev_get_drvdata(dev);
|
|
struct msm_fb_data_type *mfd = (struct msm_fb_data_type *)fbi->par;
|
|
struct mdp3_session_data *mdp3_session = NULL;
|
|
u64 vsync_ticks;
|
|
int rc;
|
|
|
|
if (!mfd || !mfd->mdp.private1)
|
|
return -EAGAIN;
|
|
|
|
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
|
|
|
|
vsync_ticks = ktime_to_ns(mdp3_session->vsync_time);
|
|
|
|
pr_debug("fb%d vsync=%llu", mfd->index, vsync_ticks);
|
|
rc = scnprintf(buf, PAGE_SIZE, "VSYNC=%llu\n", vsync_ticks);
|
|
return rc;
|
|
}
|
|
|
|
static DEVICE_ATTR(vsync_event, S_IRUGO, mdp3_vsync_show_event, NULL);
|
|
|
|
static struct attribute *vsync_fs_attrs[] = {
|
|
&dev_attr_vsync_event.attr,
|
|
NULL,
|
|
};
|
|
|
|
static struct attribute_group vsync_fs_attr_group = {
|
|
.attrs = vsync_fs_attrs,
|
|
};
|
|
|
|
static int mdp3_ctrl_clk_enable(struct msm_fb_data_type *mfd, int enable)
|
|
{
|
|
struct mdp3_session_data *session;
|
|
struct mdss_panel_data *panel;
|
|
struct dsi_panel_clk_ctrl clk_ctrl;
|
|
int rc = 0;
|
|
|
|
pr_debug("mdp3_ctrl_clk_enable %d\n", enable);
|
|
|
|
session = mfd->mdp.private1;
|
|
panel = session->panel;
|
|
|
|
if (!panel->event_handler)
|
|
return 0;
|
|
|
|
if ((enable && session->clk_on == 0) ||
|
|
(!enable && session->clk_on == 1)) {
|
|
clk_ctrl.client = DSI_CLK_REQ_MDP_CLIENT;
|
|
clk_ctrl.state = enable;
|
|
rc = panel->event_handler(panel,
|
|
MDSS_EVENT_PANEL_CLK_CTRL, (void *)&clk_ctrl);
|
|
rc |= mdp3_res_update(enable, 1, MDP3_CLIENT_DMA_P);
|
|
} else {
|
|
pr_debug("enable = %d, clk_on=%d\n", enable, session->clk_on);
|
|
}
|
|
|
|
session->clk_on = enable;
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_ctrl_res_req_bus(struct msm_fb_data_type *mfd, int status)
|
|
{
|
|
int rc = 0;
|
|
if (status) {
|
|
struct mdss_panel_info *panel_info = mfd->panel_info;
|
|
u64 ab = 0;
|
|
u64 ib = 0;
|
|
ab = panel_info->xres * panel_info->yres * 4 * 2;
|
|
ab *= panel_info->mipi.frame_rate;
|
|
ib = (ab * 3) / 2;
|
|
rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, ab, ib);
|
|
} else {
|
|
rc = mdp3_bus_scale_set_quota(MDP3_CLIENT_DMA_P, 0, 0);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_ctrl_res_req_clk(struct msm_fb_data_type *mfd, int status)
|
|
{
|
|
int rc = 0;
|
|
if (status) {
|
|
|
|
mdp3_clk_set_rate(MDP3_CLK_MDP_SRC, MDP_CORE_CLK_RATE_SVS,
|
|
MDP3_CLIENT_DMA_P);
|
|
mdp3_clk_set_rate(MDP3_CLK_VSYNC, MDP_VSYNC_CLK_RATE,
|
|
MDP3_CLIENT_DMA_P);
|
|
|
|
rc = mdp3_res_update(1, 1, MDP3_CLIENT_DMA_P);
|
|
if (rc) {
|
|
pr_err("mdp3 clk enable fail\n");
|
|
return rc;
|
|
}
|
|
} else {
|
|
rc = mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P);
|
|
if (rc)
|
|
pr_err("mdp3 clk disable fail\n");
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_ctrl_get_intf_type(struct msm_fb_data_type *mfd)
|
|
{
|
|
int type;
|
|
switch (mfd->panel.type) {
|
|
case MIPI_VIDEO_PANEL:
|
|
type = MDP3_DMA_OUTPUT_SEL_DSI_VIDEO;
|
|
break;
|
|
case MIPI_CMD_PANEL:
|
|
type = MDP3_DMA_OUTPUT_SEL_DSI_CMD;
|
|
break;
|
|
case LCDC_PANEL:
|
|
type = MDP3_DMA_OUTPUT_SEL_LCDC;
|
|
break;
|
|
default:
|
|
type = MDP3_DMA_OUTPUT_SEL_MAX;
|
|
}
|
|
return type;
|
|
}
|
|
|
|
static int mdp3_ctrl_get_source_format(u32 imgType)
|
|
{
|
|
int format;
|
|
switch (imgType) {
|
|
case MDP_RGB_565:
|
|
format = MDP3_DMA_IBUF_FORMAT_RGB565;
|
|
break;
|
|
case MDP_RGB_888:
|
|
format = MDP3_DMA_IBUF_FORMAT_RGB888;
|
|
break;
|
|
case MDP_ARGB_8888:
|
|
case MDP_RGBA_8888:
|
|
format = MDP3_DMA_IBUF_FORMAT_XRGB8888;
|
|
break;
|
|
default:
|
|
format = MDP3_DMA_IBUF_FORMAT_UNDEFINED;
|
|
}
|
|
return format;
|
|
}
|
|
|
|
static int mdp3_ctrl_get_pack_pattern(u32 imgType)
|
|
{
|
|
int packPattern = MDP3_DMA_OUTPUT_PACK_PATTERN_RGB;
|
|
if (imgType == MDP_RGBA_8888)
|
|
packPattern = MDP3_DMA_OUTPUT_PACK_PATTERN_BGR;
|
|
return packPattern;
|
|
}
|
|
|
|
static int mdp3_ctrl_intf_init(struct msm_fb_data_type *mfd,
|
|
struct mdp3_intf *intf)
|
|
{
|
|
int rc;
|
|
struct mdp3_intf_cfg cfg;
|
|
struct mdp3_video_intf_cfg *video = &cfg.video;
|
|
struct mdss_panel_info *p = mfd->panel_info;
|
|
int h_back_porch = p->lcdc.h_back_porch;
|
|
int h_front_porch = p->lcdc.h_front_porch;
|
|
int w = p->xres;
|
|
int v_back_porch = p->lcdc.v_back_porch;
|
|
int v_front_porch = p->lcdc.v_front_porch;
|
|
int h = p->yres;
|
|
int h_sync_skew = p->lcdc.hsync_skew;
|
|
int h_pulse_width = p->lcdc.h_pulse_width;
|
|
int v_pulse_width = p->lcdc.v_pulse_width;
|
|
int hsync_period = h_front_porch + h_back_porch + w + h_pulse_width;
|
|
int vsync_period = v_front_porch + v_back_porch + h + v_pulse_width;
|
|
vsync_period *= hsync_period;
|
|
|
|
cfg.type = mdp3_ctrl_get_intf_type(mfd);
|
|
if (cfg.type == MDP3_DMA_OUTPUT_SEL_DSI_VIDEO ||
|
|
cfg.type == MDP3_DMA_OUTPUT_SEL_LCDC) {
|
|
video->hsync_period = hsync_period;
|
|
video->hsync_pulse_width = h_pulse_width;
|
|
video->vsync_period = vsync_period;
|
|
video->vsync_pulse_width = v_pulse_width * hsync_period;
|
|
video->display_start_x = h_back_porch + h_pulse_width;
|
|
video->display_end_x = hsync_period - h_front_porch - 1;
|
|
video->display_start_y =
|
|
(v_back_porch + v_pulse_width) * hsync_period;
|
|
video->display_end_y =
|
|
vsync_period - v_front_porch * hsync_period - 1;
|
|
video->active_start_x = video->display_start_x;
|
|
video->active_end_x = video->display_end_x;
|
|
video->active_h_enable = true;
|
|
video->active_start_y = video->display_start_y;
|
|
video->active_end_y = video->display_end_y;
|
|
video->active_v_enable = true;
|
|
video->hsync_skew = h_sync_skew;
|
|
video->hsync_polarity = 1;
|
|
video->vsync_polarity = 1;
|
|
video->de_polarity = 1;
|
|
video->underflow_color = p->lcdc.underflow_clr;
|
|
} else if (cfg.type == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
|
|
cfg.dsi_cmd.primary_dsi_cmd_id = 0;
|
|
cfg.dsi_cmd.secondary_dsi_cmd_id = 1;
|
|
cfg.dsi_cmd.dsi_cmd_tg_intf_sel = 0;
|
|
} else
|
|
return -EINVAL;
|
|
|
|
if (intf->config)
|
|
rc = intf->config(intf, &cfg);
|
|
else
|
|
rc = -EINVAL;
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_ctrl_dma_init(struct msm_fb_data_type *mfd,
|
|
struct mdp3_dma *dma)
|
|
{
|
|
int rc;
|
|
struct mdss_panel_info *panel_info = mfd->panel_info;
|
|
struct fb_info *fbi = mfd->fbi;
|
|
struct fb_fix_screeninfo *fix;
|
|
struct fb_var_screeninfo *var;
|
|
struct mdp3_dma_output_config outputConfig;
|
|
struct mdp3_dma_source sourceConfig;
|
|
int frame_rate = mfd->panel_info->mipi.frame_rate;
|
|
int vbp, vfp, vspw;
|
|
int vtotal, vporch;
|
|
struct mdp3_notification dma_done_callback;
|
|
struct mdp3_tear_check te;
|
|
|
|
vbp = panel_info->lcdc.v_back_porch;
|
|
vfp = panel_info->lcdc.v_front_porch;
|
|
vspw = panel_info->lcdc.v_pulse_width;
|
|
vporch = vbp + vfp + vspw;
|
|
vtotal = vporch + panel_info->yres;
|
|
|
|
fix = &fbi->fix;
|
|
var = &fbi->var;
|
|
|
|
sourceConfig.format = mdp3_ctrl_get_source_format(mfd->fb_imgType);
|
|
sourceConfig.width = panel_info->xres;
|
|
sourceConfig.height = panel_info->yres;
|
|
sourceConfig.x = 0;
|
|
sourceConfig.y = 0;
|
|
sourceConfig.stride = fix->line_length;
|
|
sourceConfig.buf = mfd->iova;
|
|
sourceConfig.vporch = vporch;
|
|
sourceConfig.vsync_count =
|
|
MDP_VSYNC_CLK_RATE / (frame_rate * vtotal);
|
|
|
|
outputConfig.dither_en = 0;
|
|
outputConfig.out_sel = mdp3_ctrl_get_intf_type(mfd);
|
|
outputConfig.bit_mask_polarity = 0;
|
|
outputConfig.color_components_flip = 0;
|
|
outputConfig.pack_pattern = mdp3_ctrl_get_pack_pattern(mfd->fb_imgType);
|
|
outputConfig.pack_align = MDP3_DMA_OUTPUT_PACK_ALIGN_LSB;
|
|
outputConfig.color_comp_out_bits = (MDP3_DMA_OUTPUT_COMP_BITS_8 << 4) |
|
|
(MDP3_DMA_OUTPUT_COMP_BITS_8 << 2)|
|
|
MDP3_DMA_OUTPUT_COMP_BITS_8;
|
|
|
|
te.frame_rate = panel_info->mipi.frame_rate;
|
|
te.hw_vsync_mode = panel_info->mipi.hw_vsync_mode;
|
|
te.tear_check_en = panel_info->te.tear_check_en;
|
|
te.sync_cfg_height = panel_info->te.sync_cfg_height;
|
|
te.vsync_init_val = panel_info->te.vsync_init_val;
|
|
te.sync_threshold_start = panel_info->te.sync_threshold_start;
|
|
te.sync_threshold_continue = panel_info->te.sync_threshold_continue;
|
|
te.start_pos = panel_info->te.start_pos;
|
|
te.rd_ptr_irq = panel_info->te.rd_ptr_irq;
|
|
te.refx100 = panel_info->te.refx100;
|
|
|
|
if (dma->dma_config)
|
|
rc = dma->dma_config(dma, &sourceConfig, &outputConfig);
|
|
else
|
|
rc = -EINVAL;
|
|
|
|
if (outputConfig.out_sel == MDP3_DMA_OUTPUT_SEL_DSI_CMD) {
|
|
if (dma->dma_sync_config)
|
|
rc = dma->dma_sync_config(dma,
|
|
&sourceConfig, &te);
|
|
else
|
|
rc = -EINVAL;
|
|
dma_done_callback.handler = dma_done_notify_handler;
|
|
dma_done_callback.arg = mfd->mdp.private1;
|
|
dma->dma_done_notifier(dma, &dma_done_callback);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_ctrl_on(struct msm_fb_data_type *mfd)
|
|
{
|
|
int rc = 0;
|
|
struct mdp3_session_data *mdp3_session;
|
|
struct mdss_panel_data *panel;
|
|
|
|
pr_debug("mdp3_ctrl_on\n");
|
|
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
|
|
if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma ||
|
|
!mdp3_session->intf) {
|
|
pr_err("mdp3_ctrl_on no device");
|
|
return -ENODEV;
|
|
}
|
|
mutex_lock(&mdp3_session->lock);
|
|
if (mdp3_session->status) {
|
|
pr_debug("fb%d is on already", mfd->index);
|
|
goto on_error;
|
|
}
|
|
|
|
if (mdp3_session->intf->active) {
|
|
pr_debug("continuous splash screen, initialized already\n");
|
|
goto on_error;
|
|
}
|
|
|
|
mdp3_enable_regulator(true);
|
|
rc = mdp3_footswitch_ctrl(1);
|
|
if (rc) {
|
|
pr_err("fail to enable mdp footswitch ctrl\n");
|
|
goto on_error;
|
|
}
|
|
mdp3_ctrl_notifier_register(mdp3_session,
|
|
&mdp3_session->mfd->mdp_sync_pt_data.notifier);
|
|
|
|
/* request bus bandwidth before DSI DMA traffic */
|
|
rc = mdp3_ctrl_res_req_bus(mfd, 1);
|
|
if (rc) {
|
|
pr_err("fail to request bus resource\n");
|
|
goto on_error;
|
|
}
|
|
|
|
panel = mdp3_session->panel;
|
|
if (panel->event_handler) {
|
|
rc = panel->event_handler(panel, MDSS_EVENT_LINK_READY, NULL);
|
|
rc |= panel->event_handler(panel, MDSS_EVENT_UNBLANK, NULL);
|
|
rc |= panel->event_handler(panel, MDSS_EVENT_PANEL_ON, NULL);
|
|
}
|
|
if (rc) {
|
|
pr_err("fail to turn on the panel\n");
|
|
goto on_error;
|
|
}
|
|
rc = mdp3_ctrl_res_req_clk(mfd, 1);
|
|
if (rc) {
|
|
pr_err("fail to request mdp clk resource\n");
|
|
goto on_error;
|
|
}
|
|
|
|
rc = mdp3_ctrl_dma_init(mfd, mdp3_session->dma);
|
|
if (rc) {
|
|
pr_err("dma init failed\n");
|
|
goto on_error;
|
|
}
|
|
|
|
rc = mdp3_ppp_init();
|
|
if (rc) {
|
|
pr_err("ppp init failed\n");
|
|
goto on_error;
|
|
}
|
|
|
|
rc = mdp3_ctrl_intf_init(mfd, mdp3_session->intf);
|
|
if (rc) {
|
|
pr_err("display interface init failed\n");
|
|
goto on_error;
|
|
}
|
|
mdp3_session->clk_on = 1;
|
|
|
|
mdp3_session->first_commit = true;
|
|
|
|
on_error:
|
|
if (!rc)
|
|
mdp3_session->status = 1;
|
|
mutex_unlock(&mdp3_session->lock);
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_ctrl_off(struct msm_fb_data_type *mfd)
|
|
{
|
|
int rc = 0;
|
|
struct mdp3_session_data *mdp3_session;
|
|
struct mdss_panel_data *panel;
|
|
|
|
pr_debug("mdp3_ctrl_off\n");
|
|
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
|
|
if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma ||
|
|
!mdp3_session->intf) {
|
|
pr_err("mdp3_ctrl_on no device");
|
|
return -ENODEV;
|
|
}
|
|
|
|
panel = mdp3_session->panel;
|
|
mutex_lock(&mdp3_session->lock);
|
|
|
|
if (panel && panel->set_backlight)
|
|
panel->set_backlight(panel, 0);
|
|
|
|
if (!mdp3_session->status) {
|
|
pr_debug("fb%d is off already", mfd->index);
|
|
goto off_error;
|
|
}
|
|
|
|
mdp3_ctrl_clk_enable(mfd, 1);
|
|
|
|
mdp3_histogram_stop(mdp3_session, MDP_BLOCK_DMA_P);
|
|
|
|
if (panel->event_handler)
|
|
rc = panel->event_handler(panel, MDSS_EVENT_PANEL_OFF, NULL);
|
|
if (rc)
|
|
pr_err("fail to turn off the panel\n");
|
|
|
|
rc = mdp3_session->dma->stop(mdp3_session->dma, mdp3_session->intf);
|
|
if (rc)
|
|
pr_debug("fail to stop the MDP3 dma\n");
|
|
/* Wait for TG to turn off */
|
|
msleep(20);
|
|
|
|
mdp3_irq_deregister();
|
|
|
|
pr_debug("mdp3_ctrl_off stop clock\n");
|
|
if (mdp3_session->clk_on) {
|
|
rc = mdp3_res_update(0, 1, MDP3_CLIENT_DMA_P);
|
|
if (rc)
|
|
pr_err("mdp clock resource release failed\n");
|
|
|
|
pr_debug("mdp3_ctrl_off stop dsi controller\n");
|
|
if (panel->event_handler)
|
|
rc = panel->event_handler(panel,
|
|
MDSS_EVENT_BLANK, NULL);
|
|
if (rc)
|
|
pr_err("fail to turn off the panel\n");
|
|
}
|
|
|
|
mdp3_ctrl_notifier_unregister(mdp3_session,
|
|
&mdp3_session->mfd->mdp_sync_pt_data.notifier);
|
|
mdp3_enable_regulator(false);
|
|
mdp3_footswitch_ctrl(0);
|
|
mdp3_session->vsync_enabled = 0;
|
|
atomic_set(&mdp3_session->vsync_countdown, 0);
|
|
atomic_set(&mdp3_session->dma_done_cnt, 0);
|
|
mdp3_session->clk_on = 0;
|
|
mdp3_session->in_splash_screen = 0;
|
|
off_error:
|
|
mdp3_session->status = 0;
|
|
mdp3_bufq_deinit(&mdp3_session->bufq_out);
|
|
if (mdp3_session->overlay.id != MSMFB_NEW_REQUEST) {
|
|
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
|
|
mdp3_bufq_deinit(&mdp3_session->bufq_in);
|
|
}
|
|
mutex_unlock(&mdp3_session->lock);
|
|
return 0;
|
|
}
|
|
|
|
static int mdp3_ctrl_reset_cmd(struct msm_fb_data_type *mfd)
|
|
{
|
|
int rc = 0;
|
|
struct mdp3_session_data *mdp3_session;
|
|
struct mdp3_dma *mdp3_dma;
|
|
struct mdss_panel_data *panel;
|
|
struct mdp3_notification vsync_client;
|
|
|
|
pr_debug("mdp3_ctrl_reset_cmd\n");
|
|
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
|
|
if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma ||
|
|
!mdp3_session->intf) {
|
|
pr_err("mdp3_ctrl_reset no device");
|
|
return -ENODEV;
|
|
}
|
|
|
|
panel = mdp3_session->panel;
|
|
mdp3_dma = mdp3_session->dma;
|
|
mutex_lock(&mdp3_session->lock);
|
|
|
|
vsync_client = mdp3_dma->vsync_client;
|
|
|
|
rc = mdp3_dma->stop(mdp3_dma, mdp3_session->intf);
|
|
if (rc) {
|
|
pr_err("fail to stop the MDP3 dma\n");
|
|
goto reset_error;
|
|
}
|
|
|
|
rc = mdp3_iommu_enable();
|
|
if (rc) {
|
|
pr_err("fail to attach dma iommu\n");
|
|
goto reset_error;
|
|
}
|
|
|
|
mdp3_ctrl_intf_init(mfd, mdp3_session->intf);
|
|
mdp3_ctrl_dma_init(mfd, mdp3_dma);
|
|
|
|
if (vsync_client.handler)
|
|
mdp3_dma->vsync_enable(mdp3_dma, &vsync_client);
|
|
|
|
mdp3_session->first_commit = true;
|
|
mdp3_session->in_splash_screen = 0;
|
|
|
|
reset_error:
|
|
mutex_unlock(&mdp3_session->lock);
|
|
return rc;
|
|
}
|
|
|
|
|
|
static int mdp3_ctrl_reset(struct msm_fb_data_type *mfd)
|
|
{
|
|
int rc = 0;
|
|
struct mdp3_session_data *mdp3_session;
|
|
struct mdp3_dma *mdp3_dma;
|
|
struct mdss_panel_data *panel;
|
|
struct mdp3_notification vsync_client;
|
|
|
|
pr_debug("mdp3_ctrl_reset\n");
|
|
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
|
|
if (!mdp3_session || !mdp3_session->panel || !mdp3_session->dma ||
|
|
!mdp3_session->intf) {
|
|
pr_err("mdp3_ctrl_reset no device");
|
|
return -ENODEV;
|
|
}
|
|
|
|
if (mfd->panel.type == MIPI_CMD_PANEL) {
|
|
rc = mdp3_ctrl_reset_cmd(mfd);
|
|
return rc;
|
|
}
|
|
|
|
panel = mdp3_session->panel;
|
|
mdp3_dma = mdp3_session->dma;
|
|
mutex_lock(&mdp3_session->lock);
|
|
|
|
vsync_client = mdp3_dma->vsync_client;
|
|
if (panel && panel->set_backlight)
|
|
panel->set_backlight(panel, 0);
|
|
|
|
rc = panel->event_handler(panel, MDSS_EVENT_PANEL_OFF, NULL);
|
|
if (rc)
|
|
pr_err("fail to turn off panel\n");
|
|
|
|
rc = mdp3_dma->stop(mdp3_dma, mdp3_session->intf);
|
|
if (rc) {
|
|
pr_err("fail to stop the MDP3 dma %d\n", rc);
|
|
goto reset_error;
|
|
}
|
|
|
|
rc = mdp3_put_mdp_dsi_clk();
|
|
if (rc) {
|
|
pr_err("fail to release mdp clocks\n");
|
|
goto reset_error;
|
|
}
|
|
|
|
rc = panel->event_handler(panel, MDSS_EVENT_BLANK, NULL);
|
|
if (rc) {
|
|
pr_err("fail to blank the panel\n");
|
|
goto reset_error;
|
|
}
|
|
|
|
rc = mdp3_iommu_enable();
|
|
if (rc) {
|
|
pr_err("fail to attach dma iommu\n");
|
|
goto reset_error;
|
|
}
|
|
|
|
rc = panel->event_handler(panel, MDSS_EVENT_UNBLANK, NULL);
|
|
if (rc) {
|
|
pr_err("fail to unblank the panel\n");
|
|
goto reset_error;
|
|
}
|
|
|
|
rc = panel->event_handler(panel, MDSS_EVENT_PANEL_ON, NULL);
|
|
if (rc) {
|
|
pr_err("fail to turn on the panel\n");
|
|
goto reset_error;
|
|
}
|
|
|
|
rc = mdp3_get_mdp_dsi_clk();
|
|
if (rc) {
|
|
pr_err("fail to turn on mdp clks\n");
|
|
goto reset_error;
|
|
}
|
|
|
|
mdp3_ctrl_intf_init(mfd, mdp3_session->intf);
|
|
mdp3_ctrl_dma_init(mfd, mdp3_dma);
|
|
|
|
if (vsync_client.handler)
|
|
mdp3_dma->vsync_enable(mdp3_dma, &vsync_client);
|
|
|
|
mdp3_session->first_commit = true;
|
|
mdp3_session->in_splash_screen = 0;
|
|
|
|
reset_error:
|
|
mutex_unlock(&mdp3_session->lock);
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_overlay_get(struct msm_fb_data_type *mfd,
|
|
struct mdp_overlay *req)
|
|
{
|
|
int rc = 0;
|
|
struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
|
|
|
|
mutex_lock(&mdp3_session->lock);
|
|
|
|
if (mdp3_session->overlay.id == req->id)
|
|
*req = mdp3_session->overlay;
|
|
else
|
|
rc = -EINVAL;
|
|
|
|
mutex_unlock(&mdp3_session->lock);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_overlay_set(struct msm_fb_data_type *mfd,
|
|
struct mdp_overlay *req)
|
|
{
|
|
int rc = 0;
|
|
struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
|
|
struct mdp3_dma *dma = mdp3_session->dma;
|
|
struct fb_fix_screeninfo *fix;
|
|
struct fb_info *fbi = mfd->fbi;
|
|
int stride;
|
|
int format;
|
|
|
|
fix = &fbi->fix;
|
|
stride = req->src.width * ppp_bpp(req->src.format);
|
|
format = mdp3_ctrl_get_source_format(req->src.format);
|
|
|
|
mutex_lock(&mdp3_session->lock);
|
|
|
|
if (mdp3_session->overlay.id != req->id)
|
|
pr_err("overlay was not released, continue to recover\n");
|
|
|
|
mdp3_session->overlay = *req;
|
|
if (req->id == MSMFB_NEW_REQUEST) {
|
|
if (dma->source_config.stride != stride ||
|
|
dma->source_config.format != format) {
|
|
dma->source_config.format = format;
|
|
dma->source_config.stride = stride;
|
|
dma->output_config.pack_pattern =
|
|
mdp3_ctrl_get_pack_pattern(req->src.format);
|
|
dma->update_src_cfg = true;
|
|
}
|
|
mdp3_session->overlay.id = 1;
|
|
req->id = 1;
|
|
}
|
|
|
|
mutex_unlock(&mdp3_session->lock);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_overlay_unset(struct msm_fb_data_type *mfd, int ndx)
|
|
{
|
|
int rc = 0;
|
|
struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
|
|
struct fb_info *fbi = mfd->fbi;
|
|
struct fb_fix_screeninfo *fix;
|
|
int format;
|
|
|
|
fix = &fbi->fix;
|
|
format = mdp3_ctrl_get_source_format(mfd->fb_imgType);
|
|
mutex_lock(&mdp3_session->lock);
|
|
|
|
if (mdp3_session->overlay.id == ndx && ndx == 1) {
|
|
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
|
|
mdp3_bufq_deinit(&mdp3_session->bufq_in);
|
|
} else {
|
|
rc = -EINVAL;
|
|
}
|
|
|
|
mutex_unlock(&mdp3_session->lock);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_overlay_queue_buffer(struct msm_fb_data_type *mfd,
|
|
struct msmfb_overlay_data *req)
|
|
{
|
|
int rc;
|
|
struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
|
|
struct msmfb_data *img = &req->data;
|
|
struct mdp3_img_data data;
|
|
struct mdp3_dma *dma = mdp3_session->dma;
|
|
|
|
rc = mdp3_get_img(img, &data);
|
|
if (rc) {
|
|
pr_err("fail to get overlay buffer\n");
|
|
return rc;
|
|
}
|
|
|
|
if (data.len < dma->source_config.stride * dma->source_config.height) {
|
|
pr_err("buf length is smaller than required by dma configuration\n");
|
|
mdp3_put_img(&data);
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = mdp3_bufq_push(&mdp3_session->bufq_in, &data);
|
|
if (rc) {
|
|
pr_err("fail to queue the overlay buffer, buffer drop\n");
|
|
mdp3_put_img(&data);
|
|
return rc;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mdp3_overlay_play(struct msm_fb_data_type *mfd,
|
|
struct msmfb_overlay_data *req)
|
|
{
|
|
struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
|
|
int rc = 0;
|
|
|
|
pr_debug("mdp3_overlay_play req id=%x mem_id=%d\n",
|
|
req->id, req->data.memory_id);
|
|
|
|
mutex_lock(&mdp3_session->lock);
|
|
|
|
if (mdp3_session->overlay.id == MSMFB_NEW_REQUEST) {
|
|
pr_err("overlay play without overlay set first\n");
|
|
mutex_unlock(&mdp3_session->lock);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (mdss_fb_is_power_on(mfd))
|
|
rc = mdp3_overlay_queue_buffer(mfd, req);
|
|
else
|
|
rc = -EPERM;
|
|
|
|
mutex_unlock(&mdp3_session->lock);
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool update_roi(struct mdp3_rect oldROI, struct mdp_rect newROI)
|
|
{
|
|
return ((newROI.x != oldROI.x) || (newROI.y != oldROI.y) ||
|
|
(newROI.w != oldROI.w) || (newROI.h != oldROI.h));
|
|
}
|
|
|
|
bool is_roi_valid(struct mdp3_dma_source source_config, struct mdp_rect roi)
|
|
{
|
|
return ((roi.x >= source_config.x) &&
|
|
((roi.x + roi.w) <= source_config.width) &&
|
|
(roi.y >= source_config.y) &&
|
|
((roi.y + roi.h) <= source_config.height));
|
|
}
|
|
|
|
static int mdp3_ctrl_display_commit_kickoff(struct msm_fb_data_type *mfd,
|
|
struct mdp_display_commit *cmt_data)
|
|
{
|
|
struct mdp3_session_data *mdp3_session;
|
|
struct mdp3_img_data *data;
|
|
struct mdss_panel_info *panel_info;
|
|
int rc = 0;
|
|
bool reset_done = false;
|
|
struct mdss_panel_data *panel;
|
|
|
|
if (!mfd || !mfd->mdp.private1)
|
|
return -EINVAL;
|
|
|
|
panel_info = mfd->panel_info;
|
|
mdp3_session = mfd->mdp.private1;
|
|
if (!mdp3_session || !mdp3_session->dma)
|
|
return -EINVAL;
|
|
|
|
if (mdp3_bufq_count(&mdp3_session->bufq_in) == 0) {
|
|
pr_debug("no buffer in queue yet\n");
|
|
return -EPERM;
|
|
}
|
|
if (panel_info->partial_update_enabled &&
|
|
is_roi_valid(mdp3_session->dma->source_config, cmt_data->l_roi)
|
|
&& update_roi(mdp3_session->dma->roi, cmt_data->l_roi)) {
|
|
mdp3_session->dma->roi.x = cmt_data->l_roi.x;
|
|
mdp3_session->dma->roi.y = cmt_data->l_roi.y;
|
|
mdp3_session->dma->roi.w = cmt_data->l_roi.w;
|
|
mdp3_session->dma->roi.h = cmt_data->l_roi.h;
|
|
mdp3_session->dma->update_src_cfg = true;
|
|
}
|
|
|
|
panel = mdp3_session->panel;
|
|
if (mdp3_session->in_splash_screen) {
|
|
pr_debug("continuous splash screen, IOMMU not attached\n");
|
|
rc = mdp3_ctrl_reset(mfd);
|
|
if (rc) {
|
|
pr_err("fail to reset display\n");
|
|
return -EINVAL;
|
|
}
|
|
reset_done = true;
|
|
}
|
|
|
|
mutex_lock(&mdp3_session->lock);
|
|
|
|
if (!mdp3_session->status) {
|
|
pr_err("%s, display off!\n", __func__);
|
|
mutex_unlock(&mdp3_session->lock);
|
|
return -EPERM;
|
|
}
|
|
|
|
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_BEGIN);
|
|
data = mdp3_bufq_pop(&mdp3_session->bufq_in);
|
|
if (data) {
|
|
mdp3_ctrl_reset_countdown(mdp3_session, mfd);
|
|
mdp3_ctrl_clk_enable(mfd, 1);
|
|
if (mdp3_session->dma->update_src_cfg &&
|
|
panel_info->partial_update_enabled) {
|
|
panel->panel_info.roi.x = mdp3_session->dma->roi.x;
|
|
panel->panel_info.roi.y = mdp3_session->dma->roi.y;
|
|
panel->panel_info.roi.w = mdp3_session->dma->roi.w;
|
|
panel->panel_info.roi.h = mdp3_session->dma->roi.h;
|
|
rc = mdp3_session->dma->update(mdp3_session->dma,
|
|
(void *)(int)data->addr,
|
|
mdp3_session->intf, (void *)panel);
|
|
} else {
|
|
rc = mdp3_session->dma->update(mdp3_session->dma,
|
|
(void *)(int)data->addr,
|
|
mdp3_session->intf, NULL);
|
|
}
|
|
/* This is for the previous frame */
|
|
if (rc < 0) {
|
|
mdp3_ctrl_notify(mdp3_session,
|
|
MDP_NOTIFY_FRAME_TIMEOUT);
|
|
} else {
|
|
if (mdp3_ctrl_get_intf_type(mfd) ==
|
|
MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) {
|
|
mdp3_ctrl_notify(mdp3_session,
|
|
MDP_NOTIFY_FRAME_DONE);
|
|
}
|
|
}
|
|
mdp3_session->dma_active = 1;
|
|
init_completion(&mdp3_session->dma_completion);
|
|
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
|
|
mdp3_bufq_push(&mdp3_session->bufq_out, data);
|
|
}
|
|
|
|
if (mdp3_bufq_count(&mdp3_session->bufq_out) > 1) {
|
|
mdp3_release_splash_memory(mfd);
|
|
data = mdp3_bufq_pop(&mdp3_session->bufq_out);
|
|
if (data)
|
|
mdp3_put_img(data);
|
|
}
|
|
|
|
if (mdp3_session->first_commit) {
|
|
/*wait for one frame time to ensure frame is sent to panel*/
|
|
msleep(1000 / panel_info->mipi.frame_rate);
|
|
mdp3_session->first_commit = false;
|
|
}
|
|
|
|
mdp3_session->vsync_before_commit = 0;
|
|
if (reset_done && (panel && panel->set_backlight))
|
|
panel->set_backlight(panel, panel->panel_info.bl_max);
|
|
|
|
mutex_unlock(&mdp3_session->lock);
|
|
|
|
mdss_fb_update_notify_update(mfd);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void mdp3_ctrl_pan_display(struct msm_fb_data_type *mfd)
|
|
{
|
|
struct fb_info *fbi;
|
|
struct mdp3_session_data *mdp3_session;
|
|
u32 offset;
|
|
int bpp;
|
|
struct mdss_panel_info *panel_info;
|
|
static bool splash_done;
|
|
struct mdss_panel_data *panel;
|
|
|
|
int rc;
|
|
|
|
pr_debug("mdp3_ctrl_pan_display\n");
|
|
if (!mfd || !mfd->mdp.private1)
|
|
return;
|
|
|
|
panel_info = mfd->panel_info;
|
|
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
|
|
if (!mdp3_session || !mdp3_session->dma)
|
|
return;
|
|
|
|
if (mdp3_session->in_splash_screen) {
|
|
pr_debug("continuous splash screen, IOMMU not attached\n");
|
|
rc = mdp3_ctrl_reset(mfd);
|
|
if (rc) {
|
|
pr_err("fail to reset display\n");
|
|
return;
|
|
}
|
|
}
|
|
|
|
mutex_lock(&mdp3_session->lock);
|
|
|
|
if (!mdp3_session->status) {
|
|
pr_err("mdp3_ctrl_pan_display, display off!\n");
|
|
goto pan_error;
|
|
}
|
|
|
|
fbi = mfd->fbi;
|
|
|
|
bpp = fbi->var.bits_per_pixel / 8;
|
|
offset = fbi->var.xoffset * bpp +
|
|
fbi->var.yoffset * fbi->fix.line_length;
|
|
|
|
if (offset > fbi->fix.smem_len) {
|
|
pr_err("invalid fb offset=%u total length=%u\n",
|
|
offset, fbi->fix.smem_len);
|
|
goto pan_error;
|
|
}
|
|
|
|
if (mfd->fbi->screen_base) {
|
|
mdp3_ctrl_reset_countdown(mdp3_session, mfd);
|
|
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_BEGIN);
|
|
mdp3_ctrl_clk_enable(mfd, 1);
|
|
rc = mdp3_session->dma->update(mdp3_session->dma,
|
|
(void *)(int)(mfd->iova + offset),
|
|
mdp3_session->intf, NULL);
|
|
/* This is for the previous frame */
|
|
if (rc < 0) {
|
|
mdp3_ctrl_notify(mdp3_session,
|
|
MDP_NOTIFY_FRAME_TIMEOUT);
|
|
} else {
|
|
if (mdp3_ctrl_get_intf_type(mfd) ==
|
|
MDP3_DMA_OUTPUT_SEL_DSI_VIDEO) {
|
|
mdp3_ctrl_notify(mdp3_session,
|
|
MDP_NOTIFY_FRAME_DONE);
|
|
}
|
|
}
|
|
mdp3_session->dma_active = 1;
|
|
init_completion(&mdp3_session->dma_completion);
|
|
mdp3_ctrl_notify(mdp3_session, MDP_NOTIFY_FRAME_FLUSHED);
|
|
} else {
|
|
pr_debug("mdp3_ctrl_pan_display no memory, stop interface");
|
|
mdp3_clk_enable(1, 0);
|
|
mdp3_session->dma->stop(mdp3_session->dma, mdp3_session->intf);
|
|
mdp3_clk_enable(0, 0);
|
|
}
|
|
|
|
if (mdp3_session->first_commit) {
|
|
/*wait for one frame time to ensure frame is sent to panel*/
|
|
msleep(1000 / panel_info->mipi.frame_rate);
|
|
mdp3_session->first_commit = false;
|
|
}
|
|
|
|
mdp3_session->vsync_before_commit = 0;
|
|
panel = mdp3_session->panel;
|
|
if (!splash_done && (panel && panel->set_backlight)) {
|
|
panel->set_backlight(panel, panel->panel_info.bl_max);
|
|
splash_done = true;
|
|
}
|
|
|
|
|
|
pan_error:
|
|
mutex_unlock(&mdp3_session->lock);
|
|
}
|
|
|
|
static int mdp3_set_metadata(struct msm_fb_data_type *mfd,
|
|
struct msmfb_metadata *metadata_ptr)
|
|
{
|
|
int ret = 0;
|
|
switch (metadata_ptr->op) {
|
|
case metadata_op_crc:
|
|
ret = mdp3_ctrl_res_req_clk(mfd, 1);
|
|
if (ret) {
|
|
pr_err("failed to turn on mdp clks\n");
|
|
return ret;
|
|
}
|
|
ret = mdp3_misr_set(&metadata_ptr->data.misr_request);
|
|
ret = mdp3_ctrl_res_req_clk(mfd, 0);
|
|
if (ret) {
|
|
pr_err("failed to release mdp clks\n");
|
|
return ret;
|
|
}
|
|
break;
|
|
default:
|
|
pr_warn("Unsupported request to MDP SET META IOCTL.\n");
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int mdp3_get_metadata(struct msm_fb_data_type *mfd,
|
|
struct msmfb_metadata *metadata)
|
|
{
|
|
int ret = 0;
|
|
switch (metadata->op) {
|
|
case metadata_op_frame_rate:
|
|
metadata->data.panel_frame_rate =
|
|
mfd->panel_info->mipi.frame_rate;
|
|
break;
|
|
case metadata_op_get_caps:
|
|
metadata->data.caps.mdp_rev = 304;
|
|
metadata->data.caps.rgb_pipes = 0;
|
|
metadata->data.caps.vig_pipes = 0;
|
|
metadata->data.caps.dma_pipes = 1;
|
|
break;
|
|
case metadata_op_crc:
|
|
ret = mdp3_ctrl_res_req_clk(mfd, 1);
|
|
if (ret) {
|
|
pr_err("failed to turn on mdp clks\n");
|
|
return ret;
|
|
}
|
|
ret = mdp3_misr_get(&metadata->data.misr_request);
|
|
ret = mdp3_ctrl_res_req_clk(mfd, 0);
|
|
if (ret) {
|
|
pr_err("failed to release mdp clks\n");
|
|
return ret;
|
|
}
|
|
break;
|
|
case metadata_op_get_ion_fd:
|
|
if (mfd->fb_ion_handle) {
|
|
metadata->data.fbmem_ionfd =
|
|
dma_buf_fd(mfd->fbmem_buf, 0);
|
|
if (metadata->data.fbmem_ionfd < 0)
|
|
pr_err("fd allocation failed. fd = %d\n",
|
|
metadata->data.fbmem_ionfd);
|
|
}
|
|
break;
|
|
default:
|
|
pr_warn("Unsupported request to MDP GET META IOCTL.\n");
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int mdp3_validate_start_req(struct mdp_histogram_start_req *req)
|
|
{
|
|
if (req->frame_cnt >= MDP_HISTOGRAM_FRAME_COUNT_MAX) {
|
|
pr_err("%s invalid req frame_cnt\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
if (req->bit_mask >= MDP_HISTOGRAM_BIT_MASK_MAX) {
|
|
pr_err("%s invalid req bit mask\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
if (req->block != MDP_BLOCK_DMA_P ||
|
|
req->num_bins != MDP_HISTOGRAM_BIN_NUM) {
|
|
pr_err("mdp3_histogram_start invalid request\n");
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int mdp3_validate_scale_config(struct mdp_bl_scale_data *data)
|
|
{
|
|
if (data->scale > MDP_HISTOGRAM_BL_SCALE_MAX) {
|
|
pr_err("%s invalid bl_scale\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
if (data->min_lvl > MDP_HISTOGRAM_BL_LEVEL_MAX) {
|
|
pr_err("%s invalid bl_min_lvl\n", __func__);
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int mdp3_validate_csc_data(struct mdp_csc_cfg_data *data)
|
|
{
|
|
int i;
|
|
for (i = 0; i < 9; i++) {
|
|
if (data->csc_data.csc_mv[i] >=
|
|
MDP_HISTOGRAM_CSC_MATRIX_MAX)
|
|
return -EINVAL;
|
|
}
|
|
for (i = 0; i < 3; i++) {
|
|
if (data->csc_data.csc_pre_bv[i] >=
|
|
MDP_HISTOGRAM_CSC_VECTOR_MAX)
|
|
return -EINVAL;
|
|
if (data->csc_data.csc_post_bv[i] >=
|
|
MDP_HISTOGRAM_CSC_VECTOR_MAX)
|
|
return -EINVAL;
|
|
}
|
|
for (i = 0; i < 6; i++) {
|
|
if (data->csc_data.csc_pre_lv[i] >=
|
|
MDP_HISTOGRAM_CSC_VECTOR_MAX)
|
|
return -EINVAL;
|
|
if (data->csc_data.csc_post_lv[i] >=
|
|
MDP_HISTOGRAM_CSC_VECTOR_MAX)
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int mdp3_histogram_start(struct mdp3_session_data *session,
|
|
struct mdp_histogram_start_req *req)
|
|
{
|
|
int ret;
|
|
struct mdp3_dma_histogram_config histo_config;
|
|
|
|
pr_debug("mdp3_histogram_start\n");
|
|
|
|
ret = mdp3_validate_start_req(req);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!session->dma->histo_op ||
|
|
!session->dma->config_histo) {
|
|
pr_err("mdp3_histogram_start not supported\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mutex_lock(&session->histo_lock);
|
|
|
|
if (session->histo_status) {
|
|
pr_err("mdp3_histogram_start already started\n");
|
|
mutex_unlock(&session->histo_lock);
|
|
return -EBUSY;
|
|
}
|
|
|
|
mdp3_res_update(1, 0, MDP3_CLIENT_DMA_P);
|
|
ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_RESET);
|
|
if (ret) {
|
|
pr_err("mdp3_histogram_start reset error\n");
|
|
goto histogram_start_err;
|
|
}
|
|
|
|
histo_config.frame_count = req->frame_cnt;
|
|
histo_config.bit_mask = req->bit_mask;
|
|
histo_config.auto_clear_en = 1;
|
|
histo_config.bit_mask_polarity = 0;
|
|
ret = session->dma->config_histo(session->dma, &histo_config);
|
|
if (ret) {
|
|
pr_err("mdp3_histogram_start config error\n");
|
|
goto histogram_start_err;
|
|
}
|
|
|
|
ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_START);
|
|
if (ret) {
|
|
pr_err("mdp3_histogram_start config error\n");
|
|
goto histogram_start_err;
|
|
}
|
|
|
|
session->histo_status = 1;
|
|
|
|
histogram_start_err:
|
|
mdp3_res_update(0, 0, MDP3_CLIENT_DMA_P);
|
|
mutex_unlock(&session->histo_lock);
|
|
return ret;
|
|
}
|
|
|
|
static int mdp3_histogram_stop(struct mdp3_session_data *session,
|
|
u32 block)
|
|
{
|
|
int ret;
|
|
pr_debug("mdp3_histogram_stop\n");
|
|
|
|
if (!session->dma->histo_op || block != MDP_BLOCK_DMA_P) {
|
|
pr_err("mdp3_histogram_stop not supported\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
mutex_lock(&session->histo_lock);
|
|
|
|
if (!session->histo_status) {
|
|
ret = 0;
|
|
goto histogram_stop_err;
|
|
}
|
|
|
|
mdp3_clk_enable(1, 0);
|
|
ret = session->dma->histo_op(session->dma, MDP3_DMA_HISTO_OP_CANCEL);
|
|
mdp3_clk_enable(0, 0);
|
|
if (ret)
|
|
pr_err("mdp3_histogram_stop error\n");
|
|
|
|
session->histo_status = 0;
|
|
|
|
histogram_stop_err:
|
|
mutex_unlock(&session->histo_lock);
|
|
return ret;
|
|
}
|
|
|
|
static int mdp3_histogram_collect(struct mdp3_session_data *session,
|
|
struct mdp_histogram_data *hist)
|
|
{
|
|
int ret;
|
|
struct mdp3_dma_histogram_data *mdp3_histo;
|
|
|
|
pr_debug("%s\n", __func__);
|
|
if (!session->dma->get_histo) {
|
|
pr_err("mdp3_histogram_collect not supported\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!session->clk_on) {
|
|
pr_debug("mdp/dsi clock off currently\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
mutex_lock(&session->histo_lock);
|
|
|
|
if (!session->histo_status) {
|
|
pr_err("mdp3_histogram_collect not started\n");
|
|
mutex_unlock(&session->histo_lock);
|
|
return -EPERM;
|
|
}
|
|
|
|
mutex_unlock(&session->histo_lock);
|
|
|
|
mdp3_clk_enable(1, 0);
|
|
ret = session->dma->get_histo(session->dma);
|
|
mdp3_clk_enable(0, 0);
|
|
if (ret) {
|
|
pr_debug("mdp3_histogram_collect error = %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
mdp3_histo = &session->dma->histo_data;
|
|
|
|
ret = copy_to_user(hist->c0, mdp3_histo->r_data,
|
|
sizeof(uint32_t) * MDP_HISTOGRAM_BIN_NUM);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = copy_to_user(hist->c1, mdp3_histo->g_data,
|
|
sizeof(uint32_t) * MDP_HISTOGRAM_BIN_NUM);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = copy_to_user(hist->c2, mdp3_histo->b_data,
|
|
sizeof(uint32_t) * MDP_HISTOGRAM_BIN_NUM);
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = copy_to_user(hist->extra_info, mdp3_histo->extra,
|
|
sizeof(uint32_t) * 2);
|
|
if (ret)
|
|
return ret;
|
|
|
|
hist->bin_cnt = MDP_HISTOGRAM_BIN_NUM;
|
|
hist->block = MDP_BLOCK_DMA_P;
|
|
return ret;
|
|
}
|
|
|
|
static int mdp3_bl_scale_config(struct msm_fb_data_type *mfd,
|
|
struct mdp_bl_scale_data *data)
|
|
{
|
|
int ret = 0;
|
|
int curr_bl;
|
|
mutex_lock(&mfd->bl_lock);
|
|
curr_bl = mfd->bl_level;
|
|
mfd->bl_scale = data->scale;
|
|
mfd->bl_min_lvl = data->min_lvl;
|
|
pr_debug("update scale = %d, min_lvl = %d\n", mfd->bl_scale,
|
|
mfd->bl_min_lvl);
|
|
|
|
/* update current backlight to use new scaling*/
|
|
mdss_fb_set_backlight(mfd, curr_bl);
|
|
mutex_unlock(&mfd->bl_lock);
|
|
return ret;
|
|
}
|
|
|
|
static int mdp3_csc_config(struct mdp3_session_data *session,
|
|
struct mdp_csc_cfg_data *data)
|
|
{
|
|
struct mdp3_dma_color_correct_config config;
|
|
struct mdp3_dma_ccs ccs;
|
|
int ret = -EINVAL;
|
|
|
|
if (!data->csc_data.csc_mv || !data->csc_data.csc_pre_bv ||
|
|
!data->csc_data.csc_post_bv || !data->csc_data.csc_pre_lv ||
|
|
!data->csc_data.csc_post_lv) {
|
|
pr_err("%s : Invalid csc vectors", __func__);
|
|
return -EINVAL;
|
|
}
|
|
|
|
session->cc_vect_sel = (session->cc_vect_sel + 1) % 2;
|
|
|
|
config.ccs_enable = 1;
|
|
config.ccs_sel = session->cc_vect_sel;
|
|
config.pre_limit_sel = session->cc_vect_sel;
|
|
config.post_limit_sel = session->cc_vect_sel;
|
|
config.pre_bias_sel = session->cc_vect_sel;
|
|
config.post_bias_sel = session->cc_vect_sel;
|
|
config.ccs_dirty = true;
|
|
|
|
ccs.mv = data->csc_data.csc_mv;
|
|
ccs.pre_bv = data->csc_data.csc_pre_bv;
|
|
ccs.post_bv = data->csc_data.csc_post_bv;
|
|
ccs.pre_lv = data->csc_data.csc_pre_lv;
|
|
ccs.post_lv = data->csc_data.csc_post_lv;
|
|
|
|
mutex_lock(&session->lock);
|
|
mdp3_clk_enable(1, 0);
|
|
ret = session->dma->config_ccs(session->dma, &config, &ccs);
|
|
mdp3_clk_enable(0, 0);
|
|
mutex_unlock(&session->lock);
|
|
return ret;
|
|
}
|
|
|
|
static int mdp3_pp_ioctl(struct msm_fb_data_type *mfd,
|
|
void __user *argp)
|
|
{
|
|
int ret = -EINVAL;
|
|
struct msmfb_mdp_pp mdp_pp;
|
|
struct mdp_lut_cfg_data *lut;
|
|
struct mdp3_session_data *mdp3_session;
|
|
|
|
if (!mfd || !mfd->mdp.private1)
|
|
return -EINVAL;
|
|
|
|
mdp3_session = mfd->mdp.private1;
|
|
|
|
ret = copy_from_user(&mdp_pp, argp, sizeof(mdp_pp));
|
|
if (ret)
|
|
return ret;
|
|
|
|
switch (mdp_pp.op) {
|
|
case mdp_bl_scale_cfg:
|
|
ret = mdp3_validate_scale_config(&mdp_pp.data.bl_scale_data);
|
|
if (ret) {
|
|
pr_err("%s: invalid scale config\n", __func__);
|
|
break;
|
|
}
|
|
ret = mdp3_bl_scale_config(mfd, (struct mdp_bl_scale_data *)
|
|
&mdp_pp.data.bl_scale_data);
|
|
break;
|
|
case mdp_op_csc_cfg:
|
|
ret = mdp3_validate_csc_data(&(mdp_pp.data.csc_cfg_data));
|
|
if (ret) {
|
|
pr_err("%s: invalid csc data\n", __func__);
|
|
break;
|
|
}
|
|
ret = mdp3_csc_config(mdp3_session,
|
|
&(mdp_pp.data.csc_cfg_data));
|
|
break;
|
|
case mdp_op_lut_cfg:
|
|
lut = &mdp_pp.data.lut_cfg_data;
|
|
if (lut->lut_type != mdp_lut_rgb) {
|
|
pr_err("Lut type %d is not supported", lut->lut_type);
|
|
return -EINVAL;
|
|
}
|
|
if (lut->data.rgb_lut_data.flags & MDP_PP_OPS_READ)
|
|
ret = mdp3_ctrl_lut_read(mfd,
|
|
&(lut->data.rgb_lut_data));
|
|
else
|
|
ret = mdp3_ctrl_lut_update(mfd,
|
|
&(lut->data.rgb_lut_data));
|
|
if (ret)
|
|
pr_err("%s: invalid rgb lut data\n", __func__);
|
|
break;
|
|
|
|
default:
|
|
pr_err("Unsupported request to MDP_PP IOCTL.\n");
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
if (!ret)
|
|
ret = copy_to_user(argp, &mdp_pp, sizeof(struct msmfb_mdp_pp));
|
|
return ret;
|
|
}
|
|
|
|
static int mdp3_histo_ioctl(struct msm_fb_data_type *mfd, u32 cmd,
|
|
void __user *argp)
|
|
{
|
|
int ret = -ENOSYS;
|
|
struct mdp_histogram_data hist;
|
|
struct mdp_histogram_start_req hist_req;
|
|
u32 block;
|
|
struct mdp3_session_data *mdp3_session;
|
|
|
|
if (!mfd || !mfd->mdp.private1)
|
|
return -EINVAL;
|
|
|
|
mdp3_session = mfd->mdp.private1;
|
|
|
|
switch (cmd) {
|
|
case MSMFB_HISTOGRAM_START:
|
|
ret = copy_from_user(&hist_req, argp, sizeof(hist_req));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = mdp3_histogram_start(mdp3_session, &hist_req);
|
|
break;
|
|
|
|
case MSMFB_HISTOGRAM_STOP:
|
|
ret = copy_from_user(&block, argp, sizeof(int));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = mdp3_histogram_stop(mdp3_session, block);
|
|
break;
|
|
|
|
case MSMFB_HISTOGRAM:
|
|
ret = copy_from_user(&hist, argp, sizeof(hist));
|
|
if (ret)
|
|
return ret;
|
|
|
|
ret = mdp3_histogram_collect(mdp3_session, &hist);
|
|
if (!ret)
|
|
ret = copy_to_user(argp, &hist, sizeof(hist));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
static int mdp3_validate_lut_data(struct fb_cmap *cmap)
|
|
{
|
|
u32 i = 0;
|
|
|
|
if (!cmap || !cmap->red || !cmap->green || !cmap->blue) {
|
|
pr_err("Invalid arguments!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
for (i = 0; i < MDP_LUT_SIZE; i++) {
|
|
if (cmap->red[i] > 0xFF || cmap->green[i] > 0xFF ||
|
|
cmap->blue[i] > 0xFF) {
|
|
pr_err("LUT value over 255 (limit) at %d index\n", i);
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mdp3_alloc_lut_buffer(struct platform_device *pdev, void **cmap)
|
|
{
|
|
struct fb_cmap *map;
|
|
|
|
map = devm_kzalloc(&pdev->dev, sizeof(struct fb_cmap), GFP_KERNEL);
|
|
if (map == NULL) {
|
|
pr_err("Failed memory allocation for cmap\n");
|
|
return -ENOMEM;
|
|
}
|
|
memset(map, 0, sizeof(struct fb_cmap));
|
|
|
|
map->red = devm_kzalloc(&pdev->dev, MDP_LUT_SIZE * sizeof(u16),
|
|
GFP_KERNEL);
|
|
if (map->red == NULL) {
|
|
pr_err("Failed cmap allocation for red\n");
|
|
goto exit_red;
|
|
}
|
|
memset(map->red, 0, sizeof(u16) * MDP_LUT_SIZE);
|
|
|
|
map->green = devm_kzalloc(&pdev->dev, MDP_LUT_SIZE * sizeof(u16),
|
|
GFP_KERNEL);
|
|
if (map->green == NULL) {
|
|
pr_err("Failed cmap allocation for green\n");
|
|
goto exit_green;
|
|
}
|
|
memset(map->green, 0, sizeof(u16) * MDP_LUT_SIZE);
|
|
|
|
map->blue = devm_kzalloc(&pdev->dev, MDP_LUT_SIZE * sizeof(u16),
|
|
GFP_KERNEL);
|
|
if (map->blue == NULL) {
|
|
pr_err("Failed cmap allocation for blue\n");
|
|
goto exit_blue;
|
|
}
|
|
memset(map->blue, 0, sizeof(u16) * MDP_LUT_SIZE);
|
|
|
|
*cmap = map;
|
|
return 0;
|
|
exit_blue:
|
|
devm_kfree(&pdev->dev, map->green);
|
|
exit_green:
|
|
devm_kfree(&pdev->dev, map->red);
|
|
exit_red:
|
|
devm_kfree(&pdev->dev, map);
|
|
return -ENOMEM;
|
|
}
|
|
|
|
static void mdp3_free_lut_buffer(struct platform_device *pdev, void **cmap)
|
|
{
|
|
struct fb_cmap *map = (struct fb_cmap *)(*cmap);
|
|
|
|
if (map == NULL)
|
|
return;
|
|
|
|
devm_kfree(&pdev->dev, map->blue);
|
|
map->blue = NULL;
|
|
devm_kfree(&pdev->dev, map->green);
|
|
map->green = NULL;
|
|
devm_kfree(&pdev->dev, map->red);
|
|
map->red = NULL;
|
|
devm_kfree(&pdev->dev, map);
|
|
map = NULL;
|
|
}
|
|
|
|
static void mdp3_lut_combine_gain(struct fb_cmap *cmap, struct mdp3_dma *dma)
|
|
{
|
|
int i = 0;
|
|
u32 r = 0, g = 0, b = 0;
|
|
|
|
if (!cmap || !dma || !dma->gc_cmap || !dma->hist_cmap ||
|
|
!dma->gc_cmap->red || !dma->gc_cmap->green ||
|
|
!dma->gc_cmap->blue || !dma->hist_cmap->red ||
|
|
!dma->hist_cmap->green || !dma->hist_cmap->blue) {
|
|
pr_err("Invalid params\n");
|
|
return;
|
|
}
|
|
|
|
for (i = 1; i < MDP_LUT_SIZE; i++) {
|
|
r = MIN(dma->gc_cmap->red[i] * dma->hist_cmap->red[i] *
|
|
mdp_lut_inverse16[i], 0xFF0000);
|
|
g = MIN(dma->gc_cmap->green[i] * dma->hist_cmap->green[i] *
|
|
mdp_lut_inverse16[i], 0xFF0000);
|
|
b = MIN(dma->gc_cmap->blue[i] * dma->hist_cmap->blue[i] *
|
|
mdp_lut_inverse16[i], 0xFF0000);
|
|
|
|
cmap->red[i] = (r >> 16) & 0xFF;
|
|
cmap->green[i] = (g >> 16) & 0xFF;
|
|
cmap->blue[i] = (b >> 16) & 0xFF;
|
|
}
|
|
}
|
|
|
|
static int mdp3_ctrl_lut_update(struct msm_fb_data_type *mfd,
|
|
struct mdp_rgb_lut_data *cfg)
|
|
{
|
|
int rc = 0;
|
|
bool data_validated = false;
|
|
struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
|
|
struct mdp3_dma *dma;
|
|
struct mdp3_dma_lut_config lut_config;
|
|
struct fb_cmap *cmap;
|
|
|
|
dma = mdp3_session->dma;
|
|
|
|
if (!dma->config_lut) {
|
|
pr_err("Config LUT not defined!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (cfg->cmap.start + cfg->cmap.len > MDP_LUT_SIZE) {
|
|
pr_err("Invalid arguments\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rc = mdp3_alloc_lut_buffer(mfd->pdev, (void **) &cmap);
|
|
if (rc) {
|
|
pr_err("No memory\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
mutex_lock(&mdp3_session->pp_lock);
|
|
rc = copy_from_user(cmap->red + cfg->cmap.start,
|
|
cfg->cmap.red, sizeof(u16) * cfg->cmap.len);
|
|
rc |= copy_from_user(cmap->green + cfg->cmap.start,
|
|
cfg->cmap.green, sizeof(u16) * cfg->cmap.len);
|
|
rc |= copy_from_user(cmap->blue + cfg->cmap.start,
|
|
cfg->cmap.blue, sizeof(u16) * cfg->cmap.len);
|
|
if (rc) {
|
|
pr_err("Copying user data failed!\n");
|
|
goto exit_err;
|
|
}
|
|
|
|
switch (cfg->lut_type) {
|
|
case mdp_rgb_lut_gc:
|
|
if (cfg->flags & MDP_PP_OPS_DISABLE) {
|
|
if (dma->lut_sts & MDP3_LUT_GC_EN)
|
|
/* Free GC cmap cache since disabled */
|
|
mdp3_free_lut_buffer(mfd->pdev,
|
|
(void **)&dma->gc_cmap);
|
|
dma->lut_sts &= ~MDP3_LUT_GC_EN;
|
|
} else if (!(dma->lut_sts & MDP3_LUT_GC_EN)) {
|
|
/* Check if values sent are valid */
|
|
rc = mdp3_validate_lut_data(cmap);
|
|
if (rc) {
|
|
pr_err("Invalid GC LUT data\n");
|
|
goto exit_err;
|
|
}
|
|
data_validated = true;
|
|
|
|
/* Allocate GC cmap cache to store values */
|
|
rc = mdp3_alloc_lut_buffer(mfd->pdev,
|
|
(void **)&dma->gc_cmap);
|
|
if (rc) {
|
|
pr_err("GC LUT config failed\n");
|
|
goto exit_err;
|
|
}
|
|
dma->lut_sts |= MDP3_LUT_GC_EN;
|
|
}
|
|
/*
|
|
* Copy the GC values from userspace to maintain the
|
|
* correct values user intended to program in cache.
|
|
* The values programmed in HW might factor in presence
|
|
* of other LUT modifying features hence can be
|
|
* different from these user given values.
|
|
*/
|
|
if (dma->lut_sts & MDP3_LUT_GC_EN) {
|
|
/* Validate LUT data if not yet validated */
|
|
if (!data_validated) {
|
|
rc = mdp3_validate_lut_data(cmap);
|
|
if (rc) {
|
|
pr_err("Invalid GC LUT data\n");
|
|
goto exit_err;
|
|
}
|
|
}
|
|
memcpy(dma->gc_cmap, cmap,
|
|
sizeof(struct fb_cmap));
|
|
}
|
|
break;
|
|
case mdp_rgb_lut_hist:
|
|
if (cfg->flags & MDP_PP_OPS_DISABLE) {
|
|
if (dma->lut_sts & MDP3_LUT_HIST_EN)
|
|
/* Free HIST cmap cache since disabled */
|
|
mdp3_free_lut_buffer(mfd->pdev,
|
|
(void **)&dma->hist_cmap);
|
|
dma->lut_sts &= ~MDP3_LUT_HIST_EN;
|
|
} else if (!(dma->lut_sts & MDP3_LUT_HIST_EN)) {
|
|
/* Check if values sent are valid */
|
|
rc = mdp3_validate_lut_data(cmap);
|
|
if (rc) {
|
|
pr_err("Invalid HIST LUT data\n");
|
|
goto exit_err;
|
|
}
|
|
data_validated = true;
|
|
|
|
/* Allocate HIST cmap cache to store values */
|
|
rc = mdp3_alloc_lut_buffer(mfd->pdev,
|
|
(void **)&dma->hist_cmap);
|
|
if (rc) {
|
|
pr_err("HIST LUT config failed\n");
|
|
goto exit_err;
|
|
}
|
|
dma->lut_sts |= MDP3_LUT_HIST_EN;
|
|
}
|
|
/*
|
|
* Copy the HIST LUT values from userspace to maintain
|
|
* correct values user intended to program in cache.
|
|
* The values programmed in HW might factor in presence
|
|
* of other LUT modifying features hence can be
|
|
* different from these user given values.
|
|
*/
|
|
if (dma->lut_sts & MDP3_LUT_HIST_EN) {
|
|
/* Validate LUT data if not yet validated */
|
|
if (!data_validated) {
|
|
rc = mdp3_validate_lut_data(cmap);
|
|
if (rc) {
|
|
pr_err("Invalid H LUT data\n");
|
|
goto exit_err;
|
|
}
|
|
}
|
|
memcpy(dma->hist_cmap, cmap,
|
|
sizeof(struct fb_cmap));
|
|
}
|
|
break;
|
|
default:
|
|
pr_err("Invalid lut type: %u\n", cfg->lut_type);
|
|
rc = -EINVAL;
|
|
goto exit_err;
|
|
}
|
|
|
|
/*
|
|
* In case both GC LUT and HIST LUT need to be programmed the gains
|
|
* of each the individual LUTs need to be applied onto a single LUT
|
|
* and applied in HW
|
|
*/
|
|
if (dma->lut_sts & MDP3_LUT_HIST_GC_EN)
|
|
mdp3_lut_combine_gain(cmap, dma);
|
|
|
|
lut_config.lut_enable = 7;
|
|
lut_config.lut_sel = mdp3_session->lut_sel;
|
|
lut_config.lut_position = 0;
|
|
lut_config.lut_dirty = true;
|
|
|
|
mutex_lock(&mdp3_session->lock);
|
|
|
|
if (!mdp3_session->status) {
|
|
pr_err("display off!\n");
|
|
mutex_unlock(&mdp3_session->lock);
|
|
rc = -EPERM;
|
|
goto exit_err;
|
|
}
|
|
|
|
mdp3_clk_enable(1, 0);
|
|
rc = dma->config_lut(dma, &lut_config, cmap);
|
|
mdp3_clk_enable(0, 0);
|
|
if (rc)
|
|
pr_err("mdp3_ctrl_lut_update failed\n");
|
|
|
|
mdp3_session->lut_sel = (mdp3_session->lut_sel + 1) % 2;
|
|
|
|
mutex_unlock(&mdp3_session->lock);
|
|
exit_err:
|
|
mutex_unlock(&mdp3_session->pp_lock);
|
|
mdp3_free_lut_buffer(mfd->pdev, (void **) &cmap);
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_ctrl_lut_read(struct msm_fb_data_type *mfd,
|
|
struct mdp_rgb_lut_data *cfg)
|
|
{
|
|
int rc = 0;
|
|
struct fb_cmap *cmap;
|
|
struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
|
|
struct mdp3_dma *dma = mdp3_session->dma;
|
|
|
|
switch (cfg->lut_type) {
|
|
case mdp_rgb_lut_gc:
|
|
if (!dma->gc_cmap) {
|
|
pr_err("GC not programmed\n");
|
|
return -EPERM;
|
|
}
|
|
cmap = dma->gc_cmap;
|
|
break;
|
|
case mdp_rgb_lut_hist:
|
|
if (!dma->hist_cmap) {
|
|
pr_err("Hist LUT not programmed\n");
|
|
return -EPERM;
|
|
}
|
|
cmap = dma->hist_cmap;
|
|
break;
|
|
default:
|
|
pr_err("Invalid lut type %u\n", cfg->lut_type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
cfg->cmap.start = cmap->start;
|
|
cfg->cmap.len = cmap->len;
|
|
|
|
mutex_lock(&mdp3_session->pp_lock);
|
|
rc = copy_to_user(cfg->cmap.red, cmap->red, sizeof(u16) *
|
|
MDP_LUT_SIZE);
|
|
rc |= copy_to_user(cfg->cmap.green, cmap->green, sizeof(u16) *
|
|
MDP_LUT_SIZE);
|
|
rc |= copy_to_user(cfg->cmap.blue, cmap->blue, sizeof(u16) *
|
|
MDP_LUT_SIZE);
|
|
mutex_unlock(&mdp3_session->pp_lock);
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_overlay_prepare(struct msm_fb_data_type *mfd,
|
|
struct mdp_overlay_list __user *user_ovlist)
|
|
{
|
|
struct mdp_overlay_list ovlist;
|
|
struct mdp3_session_data *mdp3_session = mfd->mdp.private1;
|
|
struct mdp_overlay *req_list;
|
|
struct mdp_overlay *req;
|
|
int rc;
|
|
|
|
if (!mdp3_session)
|
|
return -ENODEV;
|
|
|
|
req = &mdp3_session->req_overlay;
|
|
|
|
if (copy_from_user(&ovlist, user_ovlist, sizeof(ovlist)))
|
|
return -EFAULT;
|
|
|
|
if (ovlist.num_overlays != 1) {
|
|
pr_err("OV_PREPARE failed: only 1 overlay allowed\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (copy_from_user(&req_list, ovlist.overlay_list,
|
|
sizeof(struct mdp_overlay *)))
|
|
return -EFAULT;
|
|
|
|
if (copy_from_user(req, req_list, sizeof(*req)))
|
|
return -EFAULT;
|
|
|
|
rc = mdp3_overlay_set(mfd, req);
|
|
if (!IS_ERR_VALUE(rc)) {
|
|
if (copy_to_user(req_list, req, sizeof(*req)))
|
|
return -EFAULT;
|
|
}
|
|
|
|
if (put_user(IS_ERR_VALUE(rc) ? 0 : 1,
|
|
&user_ovlist->processed_overlays))
|
|
return -EFAULT;
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_ctrl_ioctl_handler(struct msm_fb_data_type *mfd,
|
|
u32 cmd, void __user *argp)
|
|
{
|
|
int rc = -EINVAL;
|
|
struct mdp3_session_data *mdp3_session;
|
|
struct msmfb_metadata metadata;
|
|
struct mdp_overlay *req = NULL;
|
|
struct msmfb_overlay_data ov_data;
|
|
int val;
|
|
|
|
mdp3_session = (struct mdp3_session_data *)mfd->mdp.private1;
|
|
if (!mdp3_session)
|
|
return -ENODEV;
|
|
|
|
req = &mdp3_session->req_overlay;
|
|
|
|
if (!mdp3_session->status && cmd != MSMFB_METADATA_GET &&
|
|
cmd != MSMFB_HISTOGRAM_STOP) {
|
|
pr_err("mdp3_ctrl_ioctl_handler, display off!\n");
|
|
return -EPERM;
|
|
}
|
|
|
|
switch (cmd) {
|
|
case MSMFB_MDP_PP:
|
|
rc = mdp3_pp_ioctl(mfd, argp);
|
|
break;
|
|
case MSMFB_HISTOGRAM_START:
|
|
case MSMFB_HISTOGRAM_STOP:
|
|
case MSMFB_HISTOGRAM:
|
|
rc = mdp3_histo_ioctl(mfd, cmd, argp);
|
|
break;
|
|
|
|
case MSMFB_VSYNC_CTRL:
|
|
case MSMFB_OVERLAY_VSYNC_CTRL:
|
|
if (!copy_from_user(&val, argp, sizeof(val))) {
|
|
mutex_lock(&mdp3_session->lock);
|
|
mdp3_session->vsync_enabled = val;
|
|
rc = mdp3_ctrl_vsync_enable(mfd, val);
|
|
mutex_unlock(&mdp3_session->lock);
|
|
} else {
|
|
pr_err("MSMFB_OVERLAY_VSYNC_CTRL failed\n");
|
|
rc = -EFAULT;
|
|
}
|
|
break;
|
|
case MSMFB_ASYNC_BLIT:
|
|
rc = mdp3_ctrl_async_blit_req(mfd, argp);
|
|
break;
|
|
case MSMFB_BLIT:
|
|
rc = mdp3_ctrl_blit_req(mfd, argp);
|
|
break;
|
|
case MSMFB_METADATA_GET:
|
|
rc = copy_from_user(&metadata, argp, sizeof(metadata));
|
|
if (!rc)
|
|
rc = mdp3_get_metadata(mfd, &metadata);
|
|
if (!rc)
|
|
rc = copy_to_user(argp, &metadata, sizeof(metadata));
|
|
if (rc)
|
|
pr_err("mdp3_get_metadata failed (%d)\n", rc);
|
|
break;
|
|
case MSMFB_METADATA_SET:
|
|
rc = copy_from_user(&metadata, argp, sizeof(metadata));
|
|
if (!rc)
|
|
rc = mdp3_set_metadata(mfd, &metadata);
|
|
if (rc)
|
|
pr_err("mdp3_set_metadata failed (%d)\n", rc);
|
|
break;
|
|
case MSMFB_OVERLAY_GET:
|
|
rc = copy_from_user(req, argp, sizeof(*req));
|
|
if (!rc) {
|
|
rc = mdp3_overlay_get(mfd, req);
|
|
|
|
if (!IS_ERR_VALUE(rc))
|
|
rc = copy_to_user(argp, req, sizeof(*req));
|
|
}
|
|
if (rc)
|
|
pr_err("OVERLAY_GET failed (%d)\n", rc);
|
|
break;
|
|
case MSMFB_OVERLAY_SET:
|
|
rc = copy_from_user(req, argp, sizeof(*req));
|
|
if (!rc) {
|
|
rc = mdp3_overlay_set(mfd, req);
|
|
|
|
if (!IS_ERR_VALUE(rc))
|
|
rc = copy_to_user(argp, req, sizeof(*req));
|
|
}
|
|
if (rc)
|
|
pr_err("OVERLAY_SET failed (%d)\n", rc);
|
|
break;
|
|
case MSMFB_OVERLAY_UNSET:
|
|
if (!IS_ERR_VALUE(copy_from_user(&val, argp, sizeof(val))))
|
|
rc = mdp3_overlay_unset(mfd, val);
|
|
break;
|
|
case MSMFB_OVERLAY_PLAY:
|
|
rc = copy_from_user(&ov_data, argp, sizeof(ov_data));
|
|
if (!rc)
|
|
rc = mdp3_overlay_play(mfd, &ov_data);
|
|
if (rc)
|
|
pr_err("OVERLAY_PLAY failed (%d)\n", rc);
|
|
break;
|
|
case MSMFB_OVERLAY_PREPARE:
|
|
rc = mdp3_overlay_prepare(mfd, argp);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int mdp3_wait_for_dma_done(struct mdp3_session_data *session)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (session->dma_active) {
|
|
rc = wait_for_completion_timeout(&session->dma_completion,
|
|
KOFF_TIMEOUT);
|
|
if (rc > 0) {
|
|
session->dma_active = 0;
|
|
rc = 0;
|
|
} else if (rc == 0) {
|
|
rc = -ETIME;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int mdp3_update_panel_info(struct msm_fb_data_type *mfd, int mode,
|
|
int dest_ctrl)
|
|
{
|
|
int ret = 0;
|
|
struct mdp3_session_data *mdp3_session;
|
|
struct mdss_panel_data *panel;
|
|
u32 intf_type = 0;
|
|
|
|
if (!mfd || !mfd->mdp.private1)
|
|
return -EINVAL;
|
|
|
|
mdp3_session = mfd->mdp.private1;
|
|
panel = mdp3_session->panel;
|
|
|
|
if (!panel->event_handler)
|
|
return 0;
|
|
ret = panel->event_handler(panel, MDSS_EVENT_DSI_UPDATE_PANEL_DATA,
|
|
(void *)(unsigned long)mode);
|
|
if (ret)
|
|
pr_err("Dynamic switch to %s mode failed!\n",
|
|
mode ? "command" : "video");
|
|
if (mode == 1)
|
|
mfd->panel.type = MIPI_CMD_PANEL;
|
|
else
|
|
mfd->panel.type = MIPI_VIDEO_PANEL;
|
|
|
|
if (mfd->panel.type != MIPI_VIDEO_PANEL)
|
|
mdp3_session->wait_for_dma_done = mdp3_wait_for_dma_done;
|
|
|
|
intf_type = mdp3_ctrl_get_intf_type(mfd);
|
|
mdp3_session->intf->cfg.type = intf_type;
|
|
mdp3_session->intf->available = 1;
|
|
mdp3_session->intf->in_use = 1;
|
|
mdp3_res->intf[intf_type].in_use = 1;
|
|
|
|
mdp3_intf_init(mdp3_session->intf);
|
|
|
|
mdp3_session->dma->output_config.out_sel = intf_type;
|
|
mdp3_session->status = mdp3_session->intf->active;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int mdp3_ctrl_init(struct msm_fb_data_type *mfd)
|
|
{
|
|
struct device *dev = mfd->fbi->dev;
|
|
struct msm_mdp_interface *mdp3_interface = &mfd->mdp;
|
|
struct mdp3_session_data *mdp3_session = NULL;
|
|
u32 intf_type = MDP3_DMA_OUTPUT_SEL_DSI_VIDEO;
|
|
int rc;
|
|
int splash_mismatch = 0;
|
|
|
|
pr_info("mdp3_ctrl_init\n");
|
|
rc = mdp3_parse_dt_splash(mfd);
|
|
if (rc)
|
|
splash_mismatch = 1;
|
|
|
|
mdp3_interface->on_fnc = mdp3_ctrl_on;
|
|
mdp3_interface->off_fnc = mdp3_ctrl_off;
|
|
mdp3_interface->do_histogram = NULL;
|
|
mdp3_interface->cursor_update = NULL;
|
|
mdp3_interface->dma_fnc = mdp3_ctrl_pan_display;
|
|
mdp3_interface->ioctl_handler = mdp3_ctrl_ioctl_handler;
|
|
mdp3_interface->kickoff_fnc = mdp3_ctrl_display_commit_kickoff;
|
|
mdp3_interface->lut_update = NULL;
|
|
mdp3_interface->configure_panel = mdp3_update_panel_info;
|
|
|
|
mdp3_session = kzalloc(sizeof(struct mdp3_session_data), GFP_KERNEL);
|
|
if (!mdp3_session) {
|
|
pr_err("fail to allocate mdp3 private data structure");
|
|
return -ENOMEM;
|
|
}
|
|
mutex_init(&mdp3_session->lock);
|
|
INIT_WORK(&mdp3_session->clk_off_work, mdp3_dispatch_clk_off);
|
|
INIT_WORK(&mdp3_session->dma_done_work, mdp3_dispatch_dma_done);
|
|
atomic_set(&mdp3_session->vsync_countdown, 0);
|
|
mutex_init(&mdp3_session->histo_lock);
|
|
mutex_init(&mdp3_session->pp_lock);
|
|
mdp3_session->dma = mdp3_get_dma_pipe(MDP3_DMA_CAP_ALL);
|
|
if (!mdp3_session->dma) {
|
|
rc = -ENODEV;
|
|
goto init_done;
|
|
}
|
|
|
|
rc = mdp3_dma_init(mdp3_session->dma);
|
|
if (rc) {
|
|
pr_err("fail to init dma\n");
|
|
goto init_done;
|
|
}
|
|
|
|
intf_type = mdp3_ctrl_get_intf_type(mfd);
|
|
mdp3_session->intf = mdp3_get_display_intf(intf_type);
|
|
if (!mdp3_session->intf) {
|
|
rc = -ENODEV;
|
|
goto init_done;
|
|
}
|
|
rc = mdp3_intf_init(mdp3_session->intf);
|
|
if (rc) {
|
|
pr_err("fail to init interface\n");
|
|
goto init_done;
|
|
}
|
|
|
|
mdp3_session->dma->output_config.out_sel = intf_type;
|
|
mdp3_session->mfd = mfd;
|
|
mdp3_session->panel = dev_get_platdata(&mfd->pdev->dev);
|
|
mdp3_session->status = mdp3_session->intf->active;
|
|
mdp3_session->overlay.id = MSMFB_NEW_REQUEST;
|
|
mdp3_bufq_init(&mdp3_session->bufq_in);
|
|
mdp3_bufq_init(&mdp3_session->bufq_out);
|
|
mdp3_session->histo_status = 0;
|
|
mdp3_session->lut_sel = 0;
|
|
BLOCKING_INIT_NOTIFIER_HEAD(&mdp3_session->notifier_head);
|
|
|
|
init_timer(&mdp3_session->vsync_timer);
|
|
mdp3_session->vsync_timer.function = mdp3_vsync_timer_func;
|
|
mdp3_session->vsync_timer.data = (u32)mdp3_session;
|
|
mdp3_session->vsync_period = 1000 / mfd->panel_info->mipi.frame_rate;
|
|
mfd->mdp.private1 = mdp3_session;
|
|
init_completion(&mdp3_session->dma_completion);
|
|
if (intf_type != MDP3_DMA_OUTPUT_SEL_DSI_VIDEO)
|
|
mdp3_session->wait_for_dma_done = mdp3_wait_for_dma_done;
|
|
|
|
rc = sysfs_create_group(&dev->kobj, &vsync_fs_attr_group);
|
|
if (rc) {
|
|
pr_err("vsync sysfs group creation failed, ret=%d\n", rc);
|
|
goto init_done;
|
|
}
|
|
|
|
mdp3_session->vsync_event_sd = sysfs_get_dirent(dev->kobj.sd,
|
|
"vsync_event");
|
|
if (!mdp3_session->vsync_event_sd) {
|
|
pr_err("vsync_event sysfs lookup failed\n");
|
|
rc = -ENODEV;
|
|
goto init_done;
|
|
}
|
|
|
|
rc = mdp3_create_sysfs_link(dev);
|
|
if (rc)
|
|
pr_warn("problem creating link to mdp sysfs\n");
|
|
|
|
kobject_uevent(&dev->kobj, KOBJ_ADD);
|
|
pr_debug("vsync kobject_uevent(KOBJ_ADD)\n");
|
|
|
|
if (mdp3_get_cont_spash_en()) {
|
|
mdp3_session->clk_on = 1;
|
|
mdp3_session->in_splash_screen = 1;
|
|
mdp3_ctrl_notifier_register(mdp3_session,
|
|
&mdp3_session->mfd->mdp_sync_pt_data.notifier);
|
|
}
|
|
|
|
if (splash_mismatch) {
|
|
pr_err("splash memory mismatch, stop splash\n");
|
|
mdp3_ctrl_off(mfd);
|
|
}
|
|
|
|
mdp3_session->vsync_before_commit = true;
|
|
init_done:
|
|
if (IS_ERR_VALUE(rc))
|
|
kfree(mdp3_session);
|
|
|
|
return rc;
|
|
}
|