blob: 87c1565c175a79b5d7839aaccf332704ddbf6c2d [file] [log] [blame]
/*
* drivers/amlogic/drm/vpu-hw/meson_vpu_video_wrapper.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/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#ifdef CONFIG_AMLOGIC_MEDIA_CANVAS
#include <linux/amlogic/media/canvas/canvas.h>
#include <linux/amlogic/media/canvas/canvas_mgr.h>
#endif
#include <linux/amlogic/media/vfm/vframe.h>
#include "meson_vpu_pipeline.h"
#include "meson_crtc.h"
#include "meson_vpu_reg.h"
#include "meson_vpu_util.h"
#include "meson_drv.h"
#include "meson_plane.h"
static u32 video_type_get(u32 pixel_format)
{
u32 vframe_type = 0;
switch (pixel_format) {
case DRM_FORMAT_NV12:
vframe_type = VIDTYPE_VIU_NV12 | VIDTYPE_VIU_FIELD |
VIDTYPE_PROGRESSIVE;
break;
case DRM_FORMAT_NV21:
vframe_type = VIDTYPE_VIU_NV21 | VIDTYPE_VIU_FIELD |
VIDTYPE_PROGRESSIVE;
break;
case DRM_FORMAT_YUYV:
case DRM_FORMAT_YVYU:
vframe_type = VIDTYPE_VIU_422 | VIDTYPE_VIU_FIELD |
VIDTYPE_VIU_SINGLE_PLANE;
break;
default:
DRM_INFO("no support pixel format:0x%x\n", pixel_format);
break;
}
return vframe_type;
}
/* -----------------------------------------------------------------
* provider opeations
* -----------------------------------------------------------------
*/
static struct vframe_s *vp_vf_peek(void *op_arg)
{
struct meson_vpu_video *video = (struct meson_vpu_video *)op_arg;
struct vframe_s *vf = NULL;
if (kfifo_peek(&video->ready_q, &vf))
return vf;
else
return NULL;
}
static struct vframe_s *vp_vf_get(void *op_arg)
{
struct meson_vpu_video *video = (struct meson_vpu_video *)op_arg;
struct vframe_s *vf = NULL;
if (kfifo_get(&video->ready_q, &vf)) {
if (!vf)
return NULL;
return vf;
} else {
return NULL;
}
}
static void vp_vf_put(struct vframe_s *vf, void *op_arg)
{
struct meson_vpu_video *video = (struct meson_vpu_video *)op_arg;
if (!vf)
return;
kfifo_put(&video->free_q, vf);
}
static int vp_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 vp_vf_states(struct vframe_states *states, void *op_arg)
{
struct meson_vpu_video *video = (struct meson_vpu_video *)op_arg;
states->vf_pool_size = BUFFER_NUM;
states->buf_recycle_num = 0;
states->buf_free_num = kfifo_len(&video->free_q);
states->buf_avail_num = kfifo_len(&video->ready_q);
return 0;
}
static const struct vframe_operations_s vp_vf_ops = {
.peek = vp_vf_peek,
.get = vp_vf_get,
.put = vp_vf_put,
.event_cb = vp_event_cb,
.vf_states = vp_vf_states,
};
static int video_check_state(struct meson_vpu_block *vblk,
struct meson_vpu_block_state *state,
struct meson_vpu_pipeline_state *mvps)
{
struct meson_vpu_video_layer_info *plane_info;
struct meson_vpu_video *video = to_video_block(vblk);
struct meson_vpu_video_state *mvvs = to_video_state(state);
int i;
if (state->checked)
return 0;
state->checked = true;
if (!mvvs || mvvs->plane_index >= MESON_MAX_VIDEO) {
DRM_INFO("mvvs is NULL!\n");
return -1;
}
DRM_DEBUG("%s check_state called.\n", video->base.name);
plane_info = &mvps->video_plane_info[vblk->index];
mvvs->src_x = plane_info->src_x;
mvvs->src_y = plane_info->src_y;
mvvs->src_w = plane_info->src_w;
mvvs->src_h = plane_info->src_h;
mvvs->dst_x = plane_info->dst_x;
mvvs->dst_y = plane_info->dst_y;
mvvs->dst_w = plane_info->dst_w;
mvvs->dst_h = plane_info->dst_h;
mvvs->byte_stride = plane_info->byte_stride;
mvvs->phy_addr[0] = plane_info->phy_addr[0];
mvvs->phy_addr[1] = plane_info->phy_addr[1];
mvvs->pixel_format = plane_info->pixel_format;
mvvs->fb_size[0] = plane_info->fb_size[0];
mvvs->fb_size[1] = plane_info->fb_size[1];
mvvs->vf = plane_info->vf;
mvvs->is_uvm = plane_info->is_uvm;
if (!video->video_path_reg) {
kfifo_reset(&video->ready_q);
kfifo_reset(&video->free_q);
kfifo_reset(&video->display_q);
for (i = 0; i < BUFFER_NUM; i++)
kfifo_put(&video->free_q, &video->vframe[i]);
vf_reg_provider(&video->vprov);
vf_notify_receiver(video->base.name,
VFRAME_EVENT_PROVIDER_START, NULL);
video->video_path_reg = 1;
}
return 0;
}
static void video_set_state(struct meson_vpu_block *vblk,
struct meson_vpu_block_state *state)
{
struct vframe_s *vf = NULL;
struct meson_vpu_video *video = to_video_block(vblk);
struct meson_vpu_video_state *mvvs = to_video_state(state);
u32 pixel_format, src_h, byte_stride;
u64 phy_addr, phy_addr2 = 0;
if (!vblk) {
DRM_DEBUG("set_state break for NULL.\n");
return;
}
src_h = mvvs->src_h;
byte_stride = mvvs->byte_stride;
phy_addr = mvvs->phy_addr[0];
pixel_format = mvvs->pixel_format;
if (mvvs->is_uvm) {
vf = mvvs->vf;
vf->axis[0] = mvvs->dst_x;
vf->axis[1] = mvvs->dst_y;
vf->axis[2] = mvvs->dst_x + mvvs->dst_w - 1;
vf->axis[3] = mvvs->dst_y + mvvs->dst_h - 1;
vf->crop[0] = mvvs->src_y;/*crop top*/
vf->crop[1] = mvvs->src_x;/*crop left*/
/*vf->width is from mvvs->src_w which is the
*valid content so the crop of bottom and right
*could be 0
*/
vf->crop[2] = 0;/*crop bottow*/
vf->crop[3] = 0;/*crop right*/
vf->flag |= VFRAME_FLAG_VIDEO_DRM;
if (!kfifo_put(&video->ready_q, vf))
DRM_INFO("ready_q is full!\n");
} else {
if (pixel_format == DRM_FORMAT_NV12 ||
pixel_format == DRM_FORMAT_NV21) {
if (!mvvs->phy_addr[1])
phy_addr2 = phy_addr + byte_stride * src_h;
else
phy_addr2 = mvvs->phy_addr[1];
}
if (kfifo_get(&video->free_q, &vf) && vf) {
memset(vf, 0, sizeof(struct vframe_s));
vf->width = mvvs->src_w;
vf->height = mvvs->src_h;
vf->source_type = VFRAME_SOURCE_TYPE_OTHERS;
vf->source_mode = VFRAME_SOURCE_MODE_OTHERS;
vf->bitdepth = BITDEPTH_Y8 | BITDEPTH_U8 | BITDEPTH_V8;
vf->type = video_type_get(pixel_format);
vf->axis[0] = mvvs->dst_x;
vf->axis[1] = mvvs->dst_y;
vf->axis[2] = mvvs->dst_x + mvvs->dst_w - 1;
vf->axis[3] = mvvs->dst_y + mvvs->dst_h - 1;
vf->crop[0] = mvvs->src_y;/*crop top*/
vf->crop[1] = mvvs->src_x;/*crop left*/
/*vf->width is from mvvs->src_w which is the
*valid content so the crop of bottom and right
*could be 0
*/
vf->crop[2] = 0;/*crop bottow*/
vf->crop[3] = 0;/*crop right*/
vf->flag |= VFRAME_FLAG_VIDEO_DRM;
/*need sync with vpp*/
vf->canvas0Addr = (u32)-1;
/*Todo: if canvas0_config.endian = 1
*supprot little endian is okay,could be removed.
*/
vf->flag |= VFRAME_FLAG_VIDEO_LINEAR;
vf->plane_num = 1;
vf->canvas0_config[0].phy_addr = phy_addr;
vf->canvas0_config[0].width = byte_stride;
vf->canvas0_config[0].height = src_h;
vf->canvas0_config[0].block_mode =
CANVAS_BLKMODE_LINEAR;
/*big endian default support*/
vf->canvas0_config[0].endian = 0;
if (pixel_format == DRM_FORMAT_NV12 ||
pixel_format == DRM_FORMAT_NV21) {
vf->plane_num = 2;
vf->canvas0_config[1].phy_addr = phy_addr2;
vf->canvas0_config[1].width = byte_stride;
vf->canvas0_config[1].height = src_h / 2;
vf->canvas0_config[1].block_mode =
CANVAS_BLKMODE_LINEAR;
/*big endian default support*/
vf->canvas0_config[1].endian = 0;
}
DRM_DEBUG("vframe info:type(0x%x),plane_num=%d\n",
vf->type, vf->plane_num);
if (!kfifo_put(&video->ready_q, vf))
DRM_INFO("ready_q is full!\n");
} else {
DRM_INFO("free_q get fail!");
}
}
DRM_DEBUG("plane_index=%d,HW-video=%d, byte_stride=%d\n",
mvvs->plane_index, vblk->index, byte_stride);
DRM_DEBUG("phy_addr=0x%pa,phy_addr2=0x%pa\n",
&phy_addr, &phy_addr2);
DRM_DEBUG("%s set_state done.\n", video->base.name);
}
static void video_hw_enable(struct meson_vpu_block *vblk)
{
struct meson_vpu_video *video = to_video_block(vblk);
if (!video) {
DRM_DEBUG("enable break for NULL.\n");
return;
}
if (!video->video_enabled) {
set_video_enabled(1, vblk->index);
video->video_enabled = 1;
}
DRM_DEBUG("%s enable done.\n", video->base.name);
}
static void video_hw_disable(struct meson_vpu_block *vblk)
{
struct meson_vpu_video *video = to_video_block(vblk);
if (!video) {
DRM_DEBUG("disable break for NULL.\n");
return;
}
if (video->video_enabled) {
set_video_enabled(0, vblk->index);
video->video_enabled = 0;
}
if (video->video_path_reg) {
vf_unreg_provider(&video->vprov);
video->video_path_reg = 0;
}
DRM_DEBUG("%s disable done.\n", video->base.name);
}
static void video_dump_register(struct meson_vpu_block *vblk,
struct seq_file *seq)
{
}
static void video_hw_init(struct meson_vpu_block *vblk)
{
struct meson_vpu_video *video = to_video_block(vblk);
int i;
if (!vblk || !video) {
DRM_DEBUG("%s break for NULL.\n", __func__);
return;
}
INIT_KFIFO(video->ready_q);
INIT_KFIFO(video->free_q);
INIT_KFIFO(video->display_q);
kfifo_reset(&video->ready_q);
kfifo_reset(&video->free_q);
kfifo_reset(&video->display_q);
for (i = 0; i < BUFFER_NUM; i++)
kfifo_put(&video->free_q, &video->vframe[i]);
if (vblk->id == VIDEO1_BLOCK)
snprintf(video->vfm_map_chain, VP_MAP_STRUCT_SIZE,
"%s %s", video->base.name,
"amvideo");
else if (vblk->id == VIDEO2_BLOCK)
snprintf(video->vfm_map_chain, VP_MAP_STRUCT_SIZE,
"%s %s", video->base.name,
"videopip");
else
DRM_DEBUG("unsupported block id %d\n", vblk->id);
snprintf(video->vfm_map_id, VP_MAP_STRUCT_SIZE,
"video-map-%d", vblk->index);
vfm_map_add(video->vfm_map_id, video->vfm_map_chain);
vf_provider_init(&video->vprov,
video->base.name,
&vp_vf_ops, video);
DRM_DEBUG("%s:%s done.\n", __func__, video->base.name);
}
struct meson_vpu_block_ops video_ops = {
.check_state = video_check_state,
.update_state = video_set_state,
.enable = video_hw_enable,
.disable = video_hw_disable,
.dump_register = video_dump_register,
.init = video_hw_init,
};