268 lines
6.7 KiB
C
268 lines
6.7 KiB
C
|
/*
|
||
|
* Copyright (c) 2017 Samsung Electronics Co., Ltd.
|
||
|
* http://www.samsung.com
|
||
|
*
|
||
|
* Cursor Async file for Samsung EXYNOS DPU driver
|
||
|
*
|
||
|
* This program is free software; you can redistribute it and/or modify
|
||
|
* it under the terms of the GNU General Public License version 2 as
|
||
|
* published by the Free Software Foundation.
|
||
|
*/
|
||
|
|
||
|
#include "decon.h"
|
||
|
#include "dpp.h"
|
||
|
|
||
|
void decon_set_cursor_reset(struct decon_device *decon,
|
||
|
struct decon_reg_data *regs)
|
||
|
{
|
||
|
if (!decon->cursor.enabled)
|
||
|
return;
|
||
|
|
||
|
mutex_lock(&decon->cursor.lock);
|
||
|
decon->cursor.unmask = false;
|
||
|
memcpy(&decon->cursor.regs, regs, sizeof(struct decon_reg_data));
|
||
|
mutex_unlock(&decon->cursor.lock);
|
||
|
}
|
||
|
|
||
|
void decon_set_cursor_unmask(struct decon_device *decon, bool unmask)
|
||
|
{
|
||
|
if (!decon->cursor.enabled)
|
||
|
return;
|
||
|
|
||
|
mutex_lock(&decon->cursor.lock);
|
||
|
decon->cursor.unmask = unmask;
|
||
|
mutex_unlock(&decon->cursor.lock);
|
||
|
}
|
||
|
|
||
|
static void decon_set_cursor_pos(struct decon_device *decon, int x, int y)
|
||
|
{
|
||
|
if (!decon->cursor.enabled)
|
||
|
return;
|
||
|
|
||
|
if (x < 0)
|
||
|
x = 0;
|
||
|
if (y < 0)
|
||
|
y = 0;
|
||
|
|
||
|
decon->cursor.xpos = x;
|
||
|
decon->cursor.ypos = y;
|
||
|
}
|
||
|
|
||
|
static int decon_set_cursor_dpp_config(struct decon_device *decon,
|
||
|
struct decon_reg_data *regs)
|
||
|
{
|
||
|
int i, ret = 0, err_cnt = 0;
|
||
|
struct v4l2_subdev *sd;
|
||
|
struct decon_win *win;
|
||
|
|
||
|
if (!decon->cursor.enabled)
|
||
|
return 0;
|
||
|
|
||
|
if (!regs->is_cursor_win[regs->cursor_win])
|
||
|
return -1;
|
||
|
|
||
|
i = regs->cursor_win;
|
||
|
win = decon->win[i];
|
||
|
if (!test_bit(win->dpp_id, &decon->cur_using_dpp))
|
||
|
return -2;
|
||
|
|
||
|
sd = decon->dpp_sd[win->dpp_id];
|
||
|
ret = v4l2_subdev_call(sd, core, ioctl,
|
||
|
DPP_WIN_CONFIG, ®s->dpp_config[i]);
|
||
|
if (ret) {
|
||
|
decon_err("failed to config (WIN%d : DPP%d)\n",
|
||
|
i, win->dpp_id);
|
||
|
regs->win_regs[i].wincon &= (~WIN_EN_F(i));
|
||
|
decon_reg_set_win_enable(decon->id, i, false);
|
||
|
if (regs->num_of_window != 0)
|
||
|
regs->num_of_window--;
|
||
|
|
||
|
clear_bit(win->dpp_id, &decon->cur_using_dpp);
|
||
|
set_bit(win->dpp_id, &decon->dpp_err_stat);
|
||
|
err_cnt++;
|
||
|
}
|
||
|
return err_cnt;
|
||
|
}
|
||
|
|
||
|
void dpu_cursor_win_update_config(struct decon_device *decon,
|
||
|
struct decon_reg_data *regs)
|
||
|
{
|
||
|
struct decon_frame src, dst;
|
||
|
struct v4l2_subdev *sd;
|
||
|
struct dpp_restriction res;
|
||
|
unsigned short cur = regs->cursor_win;
|
||
|
|
||
|
if (!decon->cursor.enabled)
|
||
|
return;
|
||
|
|
||
|
if (!decon->id) {
|
||
|
decon_dbg("%s, decon[%d] is not support cursor a-sync\n",
|
||
|
__func__, decon->id);
|
||
|
return;
|
||
|
}
|
||
|
if (!(regs->win_regs[cur].wincon & WIN_EN_F(cur))) {
|
||
|
decon_err("%s, window[%d] is not enabled\n", __func__, cur);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!regs->is_cursor_win[cur]) {
|
||
|
decon_err("%s, window[%d] is not cursor layer\n",
|
||
|
__func__, cur);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
sd = decon->dpp_sd[0];
|
||
|
v4l2_subdev_call(sd, core, ioctl, DPP_GET_RESTRICTION, &res);
|
||
|
|
||
|
memcpy(&src, ®s->dpp_config[cur].src, sizeof(struct decon_frame));
|
||
|
memcpy(&dst, ®s->dpp_config[cur].dst, sizeof(struct decon_frame));
|
||
|
|
||
|
dst.x = decon->cursor.xpos;
|
||
|
dst.y = decon->cursor.ypos;
|
||
|
|
||
|
if ((dst.x + dst.w) > decon->lcd_info->xres)
|
||
|
dst.w = dst.w - ((dst.x + dst.w) - decon->lcd_info->xres);
|
||
|
if ((dst.y + dst.h) > decon->lcd_info->yres)
|
||
|
dst.h = dst.h - ((dst.y + dst.h) - decon->lcd_info->yres);
|
||
|
|
||
|
if (dst.w > res.src_f_w.max || dst.w < res.src_f_w.min ||
|
||
|
dst.h > res.src_f_h.max || dst.h < res.src_f_h.min) {
|
||
|
decon_info("not supported cursor: [%d] [%d %d] ",
|
||
|
cur, decon->lcd_info->xres,
|
||
|
decon->lcd_info->yres);
|
||
|
decon_info("src:(%d,%d,%d,%d)", src.x, src.y, src.w, src.h);
|
||
|
decon_info("dst:(%d,%d,%d,%d) (%d,%d,%d,%d)\n",
|
||
|
regs->dpp_config[cur].dst.x,
|
||
|
regs->dpp_config[cur].dst.y,
|
||
|
regs->dpp_config[cur].dst.w,
|
||
|
regs->dpp_config[cur].dst.h,
|
||
|
dst.x, dst.y, dst.w, dst.h);
|
||
|
memcpy(&dst, ®s->dpp_config[cur].dst,
|
||
|
sizeof(struct decon_frame));
|
||
|
}
|
||
|
|
||
|
regs->win_regs[cur].start_pos = win_start_pos(dst.x, dst.y);
|
||
|
regs->win_regs[cur].end_pos = win_end_pos(dst.x, dst.y, dst.w, dst.h);
|
||
|
|
||
|
if ((dst.w != regs->dpp_config[cur].dst.w) ||
|
||
|
(dst.h != regs->dpp_config[cur].dst.h)) {
|
||
|
decon_dbg("cursor update: [%d] [%d %d] src:(%d,%d,%d,%d)",
|
||
|
cur, decon->lcd_info->xres,
|
||
|
decon->lcd_info->yres,
|
||
|
src.x, src.y, src.w, src.h);
|
||
|
decon_dbg("dst:(%d,%d,%d,%d) (%d,%d,%d,%d)\n",
|
||
|
regs->dpp_config[cur].dst.x,
|
||
|
regs->dpp_config[cur].dst.y,
|
||
|
regs->dpp_config[cur].dst.w,
|
||
|
regs->dpp_config[cur].dst.h,
|
||
|
dst.x, dst.y, dst.w, dst.h);
|
||
|
}
|
||
|
regs->dpp_config[cur].src.w = dst.w;
|
||
|
regs->dpp_config[cur].src.h = dst.h;
|
||
|
regs->dpp_config[cur].dst.x = dst.x;
|
||
|
regs->dpp_config[cur].dst.y = dst.y;
|
||
|
regs->dpp_config[cur].dst.w = dst.w;
|
||
|
regs->dpp_config[cur].dst.h = dst.h;
|
||
|
}
|
||
|
|
||
|
int decon_set_cursor_win_config(struct decon_device *decon, int x, int y)
|
||
|
{
|
||
|
int err_cnt = 0;
|
||
|
struct decon_reg_data *regs;
|
||
|
struct decon_mode_info psr;
|
||
|
int ret = 0;
|
||
|
|
||
|
DPU_EVENT_START();
|
||
|
|
||
|
if (!decon->cursor.enabled)
|
||
|
return 0;
|
||
|
|
||
|
decon_set_cursor_pos(decon, x, y);
|
||
|
|
||
|
mutex_lock(&decon->cursor.lock);
|
||
|
|
||
|
decon_to_psr_info(decon, &psr);
|
||
|
|
||
|
if (decon->state == DECON_STATE_OFF ||
|
||
|
decon->state == DECON_STATE_TUI) {
|
||
|
decon_info("decon%d: cursor win is not active :%d\n",
|
||
|
decon->id, decon->state);
|
||
|
ret = -EINVAL;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
regs = &decon->cursor.regs;
|
||
|
if (!regs) {
|
||
|
decon_err("decon%d: cursor regs is null\n",
|
||
|
decon->id);
|
||
|
ret = -EINVAL;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
if (!regs->is_cursor_win[regs->cursor_win]) {
|
||
|
decon_err("decon%d: cursor win(%d) disable\n",
|
||
|
decon->id, regs->cursor_win);
|
||
|
ret = -EINVAL;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
if (!decon->cursor.unmask) {
|
||
|
decon_dbg("decon%d: cursor win(%d) update not ready\n",
|
||
|
decon->id, regs->cursor_win);
|
||
|
ret = -EINVAL;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
if (psr.trig_mode == DECON_HW_TRIG)
|
||
|
decon_reg_set_trigger(decon->id, &psr, DECON_TRIG_DISABLE);
|
||
|
|
||
|
decon_reg_update_req_window_mask(decon->id, regs->cursor_win);
|
||
|
|
||
|
dpu_cursor_win_update_config(decon, regs);
|
||
|
|
||
|
DPU_EVENT_LOG_CURSOR(&decon->sd, regs);
|
||
|
|
||
|
err_cnt = decon_set_cursor_dpp_config(decon, regs);
|
||
|
if (err_cnt) {
|
||
|
decon_err("decon%d: cursor win(%d) during dpp_config(err_cnt:%d)\n",
|
||
|
decon->id, regs->cursor_win, err_cnt);
|
||
|
if (psr.trig_mode == DECON_HW_TRIG)
|
||
|
decon_reg_set_trigger(decon->id, &psr, DECON_TRIG_ENABLE);
|
||
|
ret = -EINVAL;
|
||
|
goto end;
|
||
|
}
|
||
|
|
||
|
/* set decon registers for each window */
|
||
|
decon_reg_set_window_control(decon->id, regs->cursor_win,
|
||
|
®s->win_regs[regs->cursor_win],
|
||
|
regs->win_regs[regs->cursor_win].winmap_state);
|
||
|
|
||
|
decon_reg_all_win_shadow_update_req(decon->id);
|
||
|
|
||
|
if (psr.trig_mode == DECON_HW_TRIG)
|
||
|
decon_reg_set_trigger(decon->id, &psr, DECON_TRIG_ENABLE);
|
||
|
|
||
|
end:
|
||
|
if (psr.trig_mode == DECON_HW_TRIG)
|
||
|
decon->cursor.unmask = false;
|
||
|
|
||
|
mutex_unlock(&decon->cursor.lock);
|
||
|
|
||
|
DPU_EVENT_LOG(DPU_EVT_CURSOR_POS, &decon->sd, start);
|
||
|
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
void dpu_init_cursor_mode(struct decon_device *decon)
|
||
|
{
|
||
|
decon->cursor.enabled = false;
|
||
|
|
||
|
if (!IS_ENABLED(CONFIG_EXYNOS_CURSOR)) {
|
||
|
decon_info("display doesn't support cursor async mode\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
decon->cursor.enabled = true;
|
||
|
decon_info("display supports cursor async mode\n");
|
||
|
}
|