blob: feb3c1b0c978e82073f95428117b3d174505badb [file] [log] [blame]
// SPDX-License-Identifier: (GPL-2.0+ OR MIT)
/*
* drivers/amlogic/media/video_processor/video_composer/videodisplay.c
*
* Copyright (C) 2017 Amlogic, Inc. 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 as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
*/
#include <linux/amlogic/meson_uvm_core.h>
#include <linux/amlogic/media/utils/am_com.h>
#ifdef CONFIG_AMLOGIC_MEDIA_VIDEO
#include <linux/amlogic/media/video_sink/video.h>
#endif
#include "videodisplay.h"
static struct timeval vsync_time;
static DEFINE_MUTEX(video_display_mutex);
static struct composer_dev *mdev[3];
#define MAX_VIDEO_COMPOSER_INSTANCE_NUM 3
static int get_count[MAX_VIDEO_COMPOSER_INSTANCE_NUM];
static unsigned int countinue_vsync_count[MAX_VD_LAYERS];
void vsync_notify_video_composer(void)
{
int i;
int count = MAX_VIDEO_COMPOSER_INSTANCE_NUM;
countinue_vsync_count[0]++;
countinue_vsync_count[1]++;
for (i = 0; i < count; i++)
get_count[i] = 0;
do_gettimeofday(&vsync_time);
}
static void vf_pop_display_q(struct composer_dev *dev, struct vframe_s *vf)
{
struct vframe_s *dis_vf = NULL;
int k = kfifo_len(&dev->display_q);
while (kfifo_len(&dev->display_q) > 0) {
if (kfifo_get(&dev->display_q, &dis_vf)) {
if (dis_vf == vf)
break;
if (!kfifo_put(&dev->display_q, dis_vf))
vc_print(dev->index, PRINT_ERROR,
"display_q is full!\n");
}
k--;
if (k < 0) {
vc_print(dev->index, PRINT_ERROR,
"can find vf in display_q\n");
break;
}
}
}
static void vc_display_q_uninit(struct composer_dev *dev)
{
struct vframe_s *dis_vf = NULL;
struct file *file_vf = NULL;
int repeat_count;
int i;
struct vd_prepare_s *vd_prepare;
vc_print(dev->index, PRINT_QUEUE_STATUS,
"%s: display_q len=%d\n",
__func__, kfifo_len(&dev->display_q));
while (kfifo_len(&dev->display_q) > 0) {
if (kfifo_get(&dev->display_q, &dis_vf)) {
vd_prepare = container_of(dis_vf,
struct vd_prepare_s,
dst_frame);
if (IS_ERR_OR_NULL(vd_prepare)) {
vc_print(dev->index, PRINT_ERROR,
"%s: prepare is NULL.\n",
__func__);
return;
}
if (dis_vf->flag
& VFRAME_FLAG_VIDEO_COMPOSER_BYPASS) {
repeat_count = dis_vf->repeat_count[dev->index];
file_vf = dis_vf->file_vf;
vc_print(dev->index, PRINT_FENCE,
"%s: repeat_count=%d, omx_index=%d\n",
__func__,
repeat_count,
dis_vf->omx_index);
for (i = 0; i <= repeat_count; i++) {
dma_buf_put((struct dma_buf *)file_vf);
dma_fence_signal(vd_prepare->release_fence);
dev->fput_count++;
}
} else if (!(dis_vf->flag
& VFRAME_FLAG_VIDEO_COMPOSER)) {
vc_print(dev->index, PRINT_ERROR,
"%s: display_q flag is null, omx_index=%d\n",
__func__, dis_vf->omx_index);
}
}
}
}
static void vc_ready_q_uninit(struct composer_dev *dev)
{
struct vframe_s *dis_vf = NULL;
struct file *file_vf = NULL;
int repeat_count;
int i;
struct vd_prepare_s *vd_prepare;
vc_print(dev->index, PRINT_QUEUE_STATUS,
"%s: ready_q len=%d\n",
__func__, kfifo_len(&dev->ready_q));
while (kfifo_len(&dev->ready_q) > 0) {
if (kfifo_get(&dev->ready_q, &dis_vf)) {
vd_prepare = container_of(dis_vf,
struct vd_prepare_s,
dst_frame);
if (IS_ERR_OR_NULL(vd_prepare)) {
vc_print(dev->index, PRINT_ERROR,
"%s: prepare is NULL.\n",
__func__);
return;
}
if (dis_vf->flag
& VFRAME_FLAG_VIDEO_COMPOSER_BYPASS) {
repeat_count = dis_vf->repeat_count[dev->index];
file_vf = dis_vf->file_vf;
for (i = 0; i <= repeat_count; i++) {
dma_buf_put((struct dma_buf *)file_vf);
dma_fence_signal(vd_prepare->release_fence);
dev->fput_count++;
}
}
}
}
}
/* -----------------------------------------------------------------
* provider opeations
* -----------------------------------------------------------------
*/
static struct vframe_s *vc_vf_peek(void *op_arg)
{
struct composer_dev *dev = (struct composer_dev *)op_arg;
struct vframe_s *vf = NULL;
struct timeval time1;
struct timeval time2;
u64 time_vsync;
int interval_time;
/*apk/sf drop 0/3 4; vc receive 1 2 5 in one vsync*/
/*apk queue 5 and wait 1, it will fence timeout*/
if (get_count[dev->index] == 2) {
vc_print(dev->index, PRINT_ERROR,
"has already get 2, can not get more\n");
return NULL;
}
time1 = dev->start_time;
time2 = vsync_time;
if (kfifo_peek(&dev->ready_q, &vf)) {
if (vf && get_count[dev->index] > 0 &&
!(vf->flag & VFRAME_FLAG_GAME_MODE)) {
time_vsync = (u64)1000000
* (time2.tv_sec - time1.tv_sec)
+ time2.tv_usec - time1.tv_usec;
interval_time = abs(time_vsync - vf->pts_us64);
vc_print(dev->index, PRINT_PERFORMANCE,
"time_vsync=%lld, vf->pts_us64=%lld\n",
time_vsync, vf->pts_us64);
//TODO
if (interval_time < 2000/*margin_time*/) {
vc_print(dev->index, PRINT_PATTERN,
"display next vsync\n");
return NULL;
}
}
if (!vf)
return NULL;
if (dev->is_drm_enable)
return vf;
else
return videocomposer_vf_peek(op_arg);
} else {
return NULL;
}
}
static struct vframe_s *vc_vf_get(void *op_arg)
{
struct composer_dev *dev = (struct composer_dev *)op_arg;
struct vframe_s *vf = NULL;
if (kfifo_get(&dev->ready_q, &vf)) {
if (!vf)
return NULL;
if (vf->flag & VFRAME_FLAG_FAKE_FRAME)
return vf;
if (!kfifo_put(&dev->display_q, vf))
vc_print(dev->index, PRINT_ERROR,
"display_q is full!\n");
if (!(vf->flag
& VFRAME_FLAG_VIDEO_COMPOSER)) {
pr_err("vc: vf_get: flag is null\n");
}
get_count[dev->index]++;
dev->fget_count++;
vc_print(dev->index, PRINT_OTHER | PRINT_PATTERN,
"%s: omx_index=%d, index_disp=%d, get_count=%d, total_get_count=%lld, vsync =%d, vf=%p\n",
__func__,
vf->omx_index,
vf->index_disp,
get_count[dev->index],
dev->fget_count,
countinue_vsync_count[dev->index],
vf);
countinue_vsync_count[dev->index] = 0;
return vf;
} else {
return NULL;
}
}
static void vc_vf_put(struct vframe_s *vf, void *op_arg)
{
int repeat_count;
int i;
struct file *file_vf;
struct composer_dev *dev = (struct composer_dev *)op_arg;
struct vd_prepare_s *vd_prepare_tmp;
if (!vf)
return;
if (dev->is_drm_enable) {
if (vf->flag & VFRAME_FLAG_FAKE_FRAME) {
vc_print(dev->index, PRINT_OTHER,
"put: fake frame\n");
return;
}
vd_prepare_tmp = container_of(vf,
struct vd_prepare_s,
dst_frame);
if (IS_ERR_OR_NULL(vd_prepare_tmp)) {
vc_print(dev->index, PRINT_ERROR,
"%s: prepare is NULL.\n",
__func__);
return;
}
repeat_count = vf->repeat_count[dev->index];
file_vf = vf->file_vf;
vf_pop_display_q(dev, vf);
for (i = 0; i <= repeat_count; i++) {
if (file_vf) {
dma_buf_put((struct dma_buf *)file_vf);
dma_fence_signal(vd_prepare_tmp->release_fence);
dev->fput_count++;
} else {
vc_print(dev->index, PRINT_ERROR,
"put: file error!!!\n");
}
}
vd_prepare_data_q_put(dev, vd_prepare_tmp);
vc_print(dev->index, PRINT_OTHER | PRINT_PATTERN,
"%s: omx_index=%d, put_count=%lld.\n",
__func__, vf->omx_index, dev->fput_count);
} else {
vf_pop_display_q(dev, vf);
videocomposer_vf_put(vf, op_arg);
}
}
static int vc_event_cb(int type, void *data, void *private_data)
{
if (type & VFRAME_EVENT_RECEIVER_PUT)
;
else if (type & VFRAME_EVENT_RECEIVER_GET)
;
else if (type & VFRAME_EVENT_RECEIVER_FRAME_WAIT)
;
return 0;
}
static int vc_vf_states(struct vframe_states *states, void *op_arg)
{
struct composer_dev *dev = (struct composer_dev *)op_arg;
states->vf_pool_size = COMPOSER_READY_POOL_SIZE;
states->buf_recycle_num = 0;
states->buf_free_num = COMPOSER_READY_POOL_SIZE
- kfifo_len(&dev->ready_q);
states->buf_avail_num = kfifo_len(&dev->ready_q);
return 0;
}
static const struct vframe_operations_s vc_vf_provider = {
.peek = vc_vf_peek,
.get = vc_vf_get,
.put = vc_vf_put,
.event_cb = vc_event_cb,
.vf_states = vc_vf_states,
};
static struct vframe_s *vd_get_vf_from_buf(struct composer_dev *dev,
struct dma_buf *buf)
{
struct vframe_s *vf = NULL;
bool is_dec_vf = false;
struct uvm_hook_mod *uhmod;
struct file_private_data *temp_file;
if (IS_ERR_OR_NULL(buf)) {
vc_print(dev->index, PRINT_ERROR,
"%s: dma_buf is NULL.\n",
__func__);
return vf;
}
is_dec_vf = is_valid_mod_type(buf, VF_SRC_DECODER);
if (is_dec_vf) {
vc_print(dev->index, PRINT_OTHER, "vf is from decoder.\n");
vf = dmabuf_get_vframe(buf);
dmabuf_put_vframe(buf);
} else {
vc_print(dev->index, PRINT_OTHER, "vf is from v4lvideo.\n");
uhmod = uvm_get_hook_mod(buf, VF_PROCESS_V4LVIDEO);
if (!uhmod) {
vc_print(dev->index, PRINT_ERROR,
"get vframe from v4lvideo failed.\n");
return vf;
}
if (IS_ERR_VALUE(uhmod) || !uhmod->arg) {
vc_print(dev->index, PRINT_ERROR,
"vframe in v4lvideo is NULL.\n");
return vf;
}
temp_file = uhmod->arg;
vf = &temp_file->vf;
uvm_put_hook_mod(buf, VF_PROCESS_V4LVIDEO);
}
return vf;
}
static void vd_disable_video_layer(struct composer_dev *dev, int val)
{
vc_print(dev->index, PRINT_ERROR, "%s: val is %d.\n", __func__, val);
if (dev->index == 0) {
_video_set_disable(val);
} else {
_videopip_set_disable(dev->index, val);
}
}
void vd_prepare_data_q_put(struct composer_dev *dev,
struct vd_prepare_s *vd_prepare)
{
if (!vd_prepare)
return;
if (!kfifo_put(&dev->vc_prepare_data_q, vd_prepare))
vc_print(dev->index, PRINT_ERROR,
"%s: vc_prepare_data_q is full!\n",
__func__);
}
struct vd_prepare_s *vd_prepare_data_q_get(struct composer_dev *dev)
{
struct vd_prepare_s *vd_prepare = NULL;
if (!kfifo_get(&dev->vc_prepare_data_q, &vd_prepare)) {
vc_print(dev->index, PRINT_ERROR,
"%s: get vc_prepare_data failed\n",
__func__);
vd_prepare = NULL;
} else {
vd_prepare->src_frame = NULL;
memset(&vd_prepare->dst_frame, 0, sizeof(struct vframe_s));
vd_prepare->release_fence = NULL;
}
return vd_prepare;
}
int vd_render_index_get(struct composer_dev *dev)
{
int reveiver_id = 0;
int render_index = 0;
if (!dev) {
pr_info("%s: dev is null.\n", __func__);
} else {
if (dev->index >= MAX_VD_LAYERS) {
vc_print(dev->index, PRINT_ERROR,
"%s: invalid param.\n",
__func__);
} else {
reveiver_id = get_receiver_id(dev->index);
if (reveiver_id >= 5)
render_index = reveiver_id - 3;
else if (reveiver_id <= 3)
render_index = reveiver_id - 2;
else
render_index = 0;
}
vc_print(dev->index, PRINT_ERROR,
"%s: render_index is %d.\n",
__func__, render_index);
}
return render_index;
}
int video_display_create_path(struct composer_dev *dev)
{
char render_layer[16] = "";
sprintf(render_layer, "video_render.%d", dev->video_render_index);
snprintf(dev->vfm_map_chain, VCOM_MAP_NAME_SIZE,
"%s %s", dev->vf_provider_name, render_layer);
snprintf(dev->vfm_map_id, VCOM_MAP_NAME_SIZE,
"vcom-map-%d", dev->index);
if (vfm_map_add(dev->vfm_map_id, dev->vfm_map_chain) < 0) {
vc_print(dev->index, PRINT_ERROR,
"%s: vcom pipeline map creation failed %s.\n",
__func__, dev->vfm_map_id);
dev->vfm_map_id[0] = 0;
return -ENOMEM;
}
vf_provider_init(&dev->vc_vf_prov, dev->vf_provider_name,
&vc_vf_provider, dev);
vf_reg_provider(&dev->vc_vf_prov);
vf_notify_receiver(dev->vf_provider_name,
VFRAME_EVENT_PROVIDER_START, NULL);
return 0;
}
int video_display_release_path(struct composer_dev *dev)
{
vf_unreg_provider(&dev->vc_vf_prov);
if (dev->vfm_map_id[0]) {
vfm_map_remove(dev->vfm_map_id);
dev->vfm_map_id[0] = 0;
}
return 0;
}
static struct composer_dev *video_display_getdev(int layer_index)
{
struct composer_dev *dev;
struct video_composer_port_s *port;
vc_print(layer_index, PRINT_OTHER,
"%s: video_composerdev_%d.\n",
__func__, layer_index);
if (layer_index >= MAX_VIDEO_COMPOSER_INSTANCE_NUM) {
vc_print(layer_index, PRINT_ERROR,
"%s: layer-%d out of range.\n",
__func__, layer_index);
return NULL;
}
if (!mdev[layer_index]) {
mutex_lock(&video_display_mutex);
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
mutex_unlock(&video_display_mutex);
vc_print(layer_index, PRINT_ERROR,
"%s: alloc dev failed.\n",
__func__);
return NULL;
}
port = video_composer_get_port(layer_index);
dev->port = port;
dev->index = port->index;
mdev[layer_index] = dev;
mutex_unlock(&video_display_mutex);
}
return mdev[layer_index];
}
static int video_display_init(int layer_index)
{
int ret = 0, i = 0;
struct composer_dev *dev;
char render_layer[16] = "";
dev = video_display_getdev(layer_index);
if (!dev) {
pr_info("%s: get dev failed.\n", __func__);
return -EBUSY;
}
mutex_lock(&video_display_mutex);
INIT_KFIFO(dev->ready_q);
INIT_KFIFO(dev->display_q);
INIT_KFIFO(dev->vc_prepare_data_q);
kfifo_reset(&dev->ready_q);
kfifo_reset(&dev->display_q);
kfifo_reset(&dev->vc_prepare_data_q);
for (i = 0; i < COMPOSER_READY_POOL_SIZE; i++)
vd_prepare_data_q_put(dev, &dev->vd_prepare[i]);
dev->fput_count = 0;
dev->drop_frame_count = 0;
dev->fget_count = 0;
dev->received_count = 0;
dev->last_file = NULL;
dev->vd_prepare_last = NULL;
dev->is_drm_enable = true;
dev->video_render_index = vd_render_index_get(dev);
memcpy(dev->vf_provider_name,
dev->port->name,
strlen(dev->port->name) + 1);
dev->port->open_count++;
do_gettimeofday(&dev->start_time);
mutex_unlock(&video_display_mutex);
vd_disable_video_layer(dev, 2);
video_set_global_output(dev->index, 1);
ret = video_display_create_path(dev);
sprintf(render_layer, "video_render.%d", dev->video_render_index);
set_video_path_select(render_layer, dev->index);
return ret;
}
static int video_display_uninit(int layer_index)
{
int ret;
struct composer_dev *dev;
dev = video_display_getdev(layer_index);
if (!dev) {
vc_print(layer_index, PRINT_ERROR,
"%s: get dev failed.\n",
__func__);
return -EBUSY;
}
if (dev->index == 0)
set_video_path_select("default", 0);
set_blackout_policy(1);
vd_disable_video_layer(dev, 1);
video_set_global_output(dev->index, 0);
ret = video_display_release_path(dev);
vc_ready_q_uninit(dev);
vc_display_q_uninit(dev);
vc_print(dev->index, PRINT_OTHER | PRINT_PATTERN,
"%s: total put count is %lld.\n",
__func__, dev->fput_count);
dev->port->open_count--;
kfree(dev);
mdev[layer_index] = NULL;
return ret;
}
int video_display_setenable(int layer_index, int is_enable)
{
int ret = 0;
struct composer_dev *dev;
if (is_enable > VIDEO_DISPLAY_ENABLE_NORMAL ||
is_enable < VIDEO_DISPLAY_ENABLE_NONE) {
vc_print(layer_index, PRINT_ERROR,
"%s: invalid param.\n",
__func__);
return -EINVAL;
}
vc_print(layer_index, PRINT_ERROR,
"%s: val=%d.\n",
__func__, is_enable);
dev = video_display_getdev(layer_index);
if (!dev) {
vc_print(layer_index, PRINT_ERROR,
"%s: get dev failed.\n",
__func__);
return -EBUSY;
}
if (dev->enable_composer == is_enable) {
vc_print(dev->index, PRINT_ERROR,
"%s: same status, no need set.\n",
__func__);
return ret;
}
dev->enable_composer = is_enable;
if (is_enable == VIDEO_DISPLAY_ENABLE_NORMAL)
ret = video_display_init(layer_index);
else
ret = video_display_uninit(layer_index);
return ret;
}
EXPORT_SYMBOL(video_display_setenable);
int video_display_setframe(int layer_index,
struct video_display_frame_info_t *frame_info,
int flags)
{
struct composer_dev *dev;
struct vframe_s *vf = NULL;
int ready_count = 0;
bool is_repeat_vf = false;
struct vd_prepare_s *vd_prepare = NULL;
if (IS_ERR_OR_NULL(frame_info)) {
vc_print(layer_index, PRINT_ERROR,
"%s: frame_info is NULL.\n",
__func__);
return -EINVAL;
}
dev = video_display_getdev(layer_index);
if (!dev) {
vc_print(layer_index, PRINT_ERROR,
"%s: get dev failed.\n",
__func__);
return -EBUSY;
}
if (dev->enable_composer != 1)
video_display_setenable(layer_index, 1);
vf = vd_get_vf_from_buf(dev, frame_info->dmabuf);
if (!vf) {
vc_print(layer_index, PRINT_ERROR,
"%s: get vf failed.\n",
__func__);
return -ENOENT;
}
get_dma_buf(frame_info->dmabuf);
dev->received_count++;
vc_print(layer_index, PRINT_OTHER | PRINT_PATTERN,
"%s: total receive_count is %lld.\n",
__func__, dev->received_count);
if (dev->last_file == (struct file *)frame_info->dmabuf)
is_repeat_vf = true;
if (is_repeat_vf) {
vd_prepare = dev->vd_prepare_last;
} else {
vd_prepare = vd_prepare_data_q_get(dev);
if (!vd_prepare) {
vc_print(dev->index, PRINT_ERROR,
"%s: get prepare_data failed.\n",
__func__);
return -ENOENT;
}
vd_prepare->src_frame = vf;
vd_prepare->dst_frame = *vf;
vd_prepare->release_fence = frame_info->release_fence;
dev->vd_prepare_last = vd_prepare;
}
vf = &vd_prepare->dst_frame;
vf->axis[0] = frame_info->dst_x;
vf->axis[1] = frame_info->dst_y;
vf->axis[2] = frame_info->dst_w + frame_info->dst_x - 1;
vf->axis[3] = frame_info->dst_h + frame_info->dst_y - 1;
vf->crop[0] = frame_info->crop_y;
vf->crop[1] = frame_info->crop_x;
vf->crop[2] = frame_info->buffer_h
- frame_info->crop_h
- frame_info->crop_y;
vf->crop[3] = frame_info->buffer_w
- frame_info->crop_w
- frame_info->crop_x;
vf->zorder = frame_info->zorder;
vf->flag |= VFRAME_FLAG_VIDEO_COMPOSER
| VFRAME_FLAG_VIDEO_COMPOSER_BYPASS;
vf->disp_pts = 0;
if (is_repeat_vf) {
vf->repeat_count[dev->index]++;
vc_print(layer_index, PRINT_ERROR,
"%s: repeat frame, repeat_count is %d.\n",
__func__, vf->repeat_count[dev->index]);
return 0;
}
dev->last_file = (struct file *)frame_info->dmabuf;
vf->file_vf = (struct file *)(frame_info->dmabuf);
vf->repeat_count[dev->index] = 0;
if (!kfifo_put(&dev->ready_q, (const struct vframe_s *)vf)) {
vc_print(layer_index, PRINT_ERROR,
"%s: ready_q is full.\n",
__func__);
return -EAGAIN;
}
ready_count = kfifo_len(&dev->ready_q);
vc_print(layer_index, PRINT_OTHER,
"%s: ready_q count is %d.\n",
__func__, ready_count);
return 0;
}
EXPORT_SYMBOL(video_display_setframe);