This change updates the parameters needed for vb2 get_userptr api in kernel 6.1. Change-Id: I43fb78c701f182e57d3f016ff54d3601f0773fff Signed-off-by: Vinod Polimera <quic_vpolimer@quicinc.com>
3629 lines
100 KiB
C
3629 lines
100 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Copyright (c) 2021-2024 Qualcomm Innovation Center, Inc. All rights reserved.
|
|
* Copyright (c) 2015-2020, The Linux Foundation. All rights reserved.
|
|
*/
|
|
#define pr_fmt(fmt) "%s:%d: " fmt, __func__, __LINE__
|
|
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/pm_runtime.h>
|
|
#include <linux/regulator/consumer.h>
|
|
#include <linux/delay.h>
|
|
#include <linux/wait.h>
|
|
#include <linux/of.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/version.h>
|
|
#include <media/v4l2-ioctl.h>
|
|
#include <media/v4l2-event.h>
|
|
#include <media/videobuf2-v4l2.h>
|
|
#include <media/v4l2-mem2mem.h>
|
|
|
|
#include "sde_rotator_inline.h"
|
|
#include "sde_rotator_base.h"
|
|
#include "sde_rotator_core.h"
|
|
#include "sde_rotator_dev.h"
|
|
#include "sde_rotator_debug.h"
|
|
#include "sde_rotator_trace.h"
|
|
|
|
/* Start v4l2 device number (default allocation) */
|
|
#define SDE_ROTATOR_BASE_DEVICE_NUMBER -1
|
|
|
|
/* Default value for early_submit flag */
|
|
#define SDE_ROTATOR_EARLY_SUBMIT 1
|
|
|
|
/* Timeout (msec) waiting for stream to turn off. */
|
|
#define SDE_ROTATOR_STREAM_OFF_TIMEOUT 500
|
|
|
|
/* acquire fence time out, following other driver fence time out practice */
|
|
#define SDE_ROTATOR_FENCE_TIMEOUT MSEC_PER_SEC
|
|
|
|
/* Timeout (msec) waiting for ctx open */
|
|
#define SDE_ROTATOR_CTX_OPEN_TIMEOUT 500
|
|
|
|
/* Rotator default fps */
|
|
#define SDE_ROTATOR_DEFAULT_FPS 60
|
|
|
|
/* Rotator rotation angles */
|
|
#define SDE_ROTATOR_DEGREE_270 270
|
|
#define SDE_ROTATOR_DEGREE_180 180
|
|
#define SDE_ROTATOR_DEGREE_90 90
|
|
|
|
/* Inline rotator qos request */
|
|
#define SDE_ROTATOR_ADD_REQUEST 1
|
|
#define SDE_ROTATOR_REMOVE_REQUEST 0
|
|
|
|
#ifndef CONFIG_MSM_SDE_ROTATOR_INIT_ONLY
|
|
static void sde_rotator_submit_handler(struct kthread_work *work);
|
|
static void sde_rotator_retire_handler(struct kthread_work *work);
|
|
#if IS_ENABLED(CONFIG_COMPAT)
|
|
static long sde_rotator_compat_ioctl32(struct file *file,
|
|
unsigned int cmd, unsigned long arg);
|
|
#endif /* CONFIG_COMPAT */
|
|
|
|
/*
|
|
* sde_rotator_ctx_from_fh - Get rotator context from v4l2 fh.
|
|
* @fh: Pointer to v4l2 fh.
|
|
*/
|
|
static inline struct sde_rotator_ctx *sde_rotator_ctx_from_fh(
|
|
struct v4l2_fh *fh)
|
|
{
|
|
return container_of(fh, struct sde_rotator_ctx, fh);
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_get_flags_from_ctx - Get low-level command flag
|
|
* @ctx: Pointer to rotator context.
|
|
*/
|
|
static uint32_t sde_rotator_get_flags_from_ctx(struct sde_rotator_ctx *ctx)
|
|
{
|
|
uint32_t ret_flags = 0;
|
|
|
|
if (ctx->rotate == SDE_ROTATOR_DEGREE_270)
|
|
ret_flags |= SDE_ROTATION_270;
|
|
else if (ctx->rotate == SDE_ROTATOR_DEGREE_180)
|
|
ret_flags |= SDE_ROTATION_180;
|
|
else if (ctx->rotate == SDE_ROTATOR_DEGREE_90)
|
|
ret_flags |= SDE_ROTATION_90;
|
|
if (ctx->hflip)
|
|
ret_flags ^= SDE_ROTATION_FLIP_LR;
|
|
if (ctx->vflip)
|
|
ret_flags ^= SDE_ROTATION_FLIP_UD;
|
|
if (ctx->secure)
|
|
ret_flags |= SDE_ROTATION_SECURE;
|
|
if (ctx->secure_camera)
|
|
ret_flags |= SDE_ROTATION_SECURE_CAMERA;
|
|
if (ctx->format_out.fmt.pix.field == V4L2_FIELD_INTERLACED &&
|
|
ctx->format_cap.fmt.pix.field == V4L2_FIELD_NONE)
|
|
ret_flags |= SDE_ROTATION_DEINTERLACE;
|
|
|
|
return ret_flags;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_get_config_from_ctx - Fill rotator configure structure.
|
|
* @ctx: Pointer to rotator ctx.
|
|
* @config: Pointer to config structure.
|
|
*/
|
|
static void sde_rotator_get_config_from_ctx(struct sde_rotator_ctx *ctx,
|
|
struct sde_rotation_config *config)
|
|
{
|
|
memset(config, 0, sizeof(struct sde_rotation_config));
|
|
config->flags = sde_rotator_get_flags_from_ctx(ctx);
|
|
config->frame_rate = (ctx->timeperframe.numerator) ?
|
|
ctx->timeperframe.denominator
|
|
/ ctx->timeperframe.numerator : 0;
|
|
config->session_id = ctx->session_id;
|
|
config->input.width = ctx->crop_out.width;
|
|
config->input.height = ctx->crop_out.height;
|
|
config->input.format = ctx->format_out.fmt.pix.pixelformat;
|
|
config->input.comp_ratio.numer = 1;
|
|
config->input.comp_ratio.denom = 1;
|
|
config->output.width = ctx->crop_cap.width;
|
|
config->output.height = ctx->crop_cap.height;
|
|
config->output.format = ctx->format_cap.fmt.pix.pixelformat;
|
|
config->output.comp_ratio.numer = 1;
|
|
config->output.comp_ratio.denom = 1;
|
|
|
|
/*
|
|
* Use compression ratio of the first buffer to estimate
|
|
* performance requirement of the session. If core layer does
|
|
* not support dynamic per buffer compression ratio recalculation,
|
|
* this configuration will determine the overall static compression
|
|
* ratio of the session.
|
|
*/
|
|
if (ctx->vbinfo_out)
|
|
config->input.comp_ratio = ctx->vbinfo_out[0].comp_ratio;
|
|
if (ctx->vbinfo_cap)
|
|
config->output.comp_ratio = ctx->vbinfo_cap[0].comp_ratio;
|
|
|
|
SDEDEV_DBG(ctx->rot_dev->dev, "config s:%d out_cr:%u/%u cap_cr:%u/%u\n",
|
|
ctx->session_id,
|
|
config->input.comp_ratio.numer,
|
|
config->input.comp_ratio.denom,
|
|
config->output.comp_ratio.numer,
|
|
config->output.comp_ratio.denom);
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_get_item_from_ctx - Fill rotator item structure.
|
|
* @ctx: Pointer to rotator ctx.
|
|
* @item: Pointer to item structure.
|
|
*/
|
|
static void sde_rotator_get_item_from_ctx(struct sde_rotator_ctx *ctx,
|
|
struct sde_rotation_item *item)
|
|
{
|
|
memset(item, 0, sizeof(struct sde_rotation_item));
|
|
item->flags = sde_rotator_get_flags_from_ctx(ctx);
|
|
item->session_id = ctx->session_id;
|
|
item->sequence_id = 0;
|
|
/* assign high/low priority */
|
|
item->wb_idx = (ctx->fh.prio >= V4L2_PRIORITY_DEFAULT) ? 0 : 1;
|
|
item->src_rect.x = ctx->crop_out.left;
|
|
item->src_rect.y = ctx->crop_out.top;
|
|
item->src_rect.w = ctx->crop_out.width;
|
|
item->src_rect.h = ctx->crop_out.height;
|
|
item->input.width = ctx->format_out.fmt.pix.width;
|
|
item->input.height = ctx->format_out.fmt.pix.height;
|
|
item->input.format = ctx->format_out.fmt.pix.pixelformat;
|
|
item->input.planes[0].fd = -1;
|
|
item->input.planes[0].offset = 0;
|
|
item->input.planes[0].stride = ctx->format_out.fmt.pix.bytesperline;
|
|
item->input.plane_count = 1;
|
|
item->input.fence = NULL;
|
|
item->input.comp_ratio.numer = 1;
|
|
item->input.comp_ratio.denom = 1;
|
|
|
|
item->dst_rect.x = ctx->crop_cap.left;
|
|
item->dst_rect.y = ctx->crop_cap.top;
|
|
item->dst_rect.w = ctx->crop_cap.width;
|
|
item->dst_rect.h = ctx->crop_cap.height;
|
|
item->output.width = ctx->format_cap.fmt.pix.width;
|
|
item->output.height = ctx->format_cap.fmt.pix.height;
|
|
item->output.format = ctx->format_cap.fmt.pix.pixelformat;
|
|
item->output.planes[0].fd = -1;
|
|
item->output.planes[0].offset = 0;
|
|
item->output.planes[0].stride = ctx->format_cap.fmt.pix.bytesperline;
|
|
item->output.plane_count = 1;
|
|
item->output.fence = NULL;
|
|
item->output.comp_ratio.numer = 1;
|
|
item->output.comp_ratio.denom = 1;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_format_recalc - Recalculate format parameters.
|
|
* @f: v4l2 format.
|
|
*/
|
|
static void sde_rotator_format_recalc(struct v4l2_format *f)
|
|
{
|
|
int ret;
|
|
struct sde_mdp_format_params *fmt;
|
|
struct sde_mdp_plane_sizes ps;
|
|
|
|
fmt = sde_get_format_params(f->fmt.pix.pixelformat);
|
|
if (!fmt) {
|
|
SDEROT_ERR("invalid format\n");
|
|
goto error_fmt;
|
|
}
|
|
|
|
ret = sde_mdp_get_plane_sizes(fmt,
|
|
f->fmt.pix.width, f->fmt.pix.height, &ps, 0, 0);
|
|
if (ret) {
|
|
SDEROT_ERR("invalid plane size\n");
|
|
goto error_fmt;
|
|
}
|
|
|
|
f->fmt.pix.bytesperline = ps.ystride[0];
|
|
f->fmt.pix.sizeimage = ps.total_size;
|
|
|
|
return;
|
|
error_fmt:
|
|
f->fmt.pix.bytesperline = 0;
|
|
f->fmt.pix.sizeimage = 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_validate_item - Check if rotator item is valid for processing.
|
|
* @ctx: Pointer to rotator ctx.
|
|
* @item: Pointer to item structure
|
|
*/
|
|
static int sde_rotator_validate_item(struct sde_rotator_ctx *ctx,
|
|
struct sde_rotation_item *item)
|
|
{
|
|
int ret;
|
|
struct sde_rot_entry_container *req;
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
req = sde_rotator_req_init(rot_dev->mgr, ctx->private, item, 1, 0);
|
|
if (IS_ERR_OR_NULL(req)) {
|
|
SDEDEV_ERR(rot_dev->dev, "fail allocate item\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
ret = sde_rotator_validate_request(rot_dev->mgr, ctx->private, req);
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
devm_kfree(rot_dev->dev, req);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_queue_setup - vb2_ops queue_setup callback.
|
|
* @q: Pointer to vb2 queue struct.
|
|
* @num_buffers: Pointer of number of buffers requested.
|
|
* @num_planes: Pointer to number of planes requested.
|
|
* @sizes: Array containing sizes of planes.
|
|
* @alloc_ctxs: Array of allocated contexts for each plane.
|
|
*/
|
|
static int sde_rotator_queue_setup(struct vb2_queue *q,
|
|
unsigned int *num_buffers, unsigned int *num_planes,
|
|
unsigned int sizes[], struct device *alloc_devs[])
|
|
{
|
|
struct sde_rotator_ctx *ctx = vb2_get_drv_priv(q);
|
|
int i;
|
|
|
|
if (!num_buffers)
|
|
return -EINVAL;
|
|
|
|
switch (q->type) {
|
|
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
|
|
sizes[0] = ctx->format_out.fmt.pix.sizeimage;
|
|
break;
|
|
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
|
sizes[0] = ctx->format_cap.fmt.pix.sizeimage;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
*num_planes = 1;
|
|
alloc_devs[0] = (struct device *)ctx;
|
|
|
|
switch (q->type) {
|
|
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
|
|
ctx->nbuf_out = *num_buffers;
|
|
kfree(ctx->vbinfo_out);
|
|
ctx->vbinfo_out = kcalloc(ctx->nbuf_out,
|
|
sizeof(struct sde_rotator_vbinfo), GFP_KERNEL);
|
|
if (!ctx->vbinfo_out)
|
|
return -ENOMEM;
|
|
for (i = 0; i < ctx->nbuf_out; i++) {
|
|
ctx->vbinfo_out[i].fd = -1;
|
|
ctx->vbinfo_out[i].comp_ratio.numer = 1;
|
|
ctx->vbinfo_out[i].comp_ratio.denom = 1;
|
|
}
|
|
break;
|
|
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
|
ctx->nbuf_cap = *num_buffers;
|
|
kfree(ctx->vbinfo_cap);
|
|
ctx->vbinfo_cap = kcalloc(ctx->nbuf_cap,
|
|
sizeof(struct sde_rotator_vbinfo), GFP_KERNEL);
|
|
if (!ctx->vbinfo_cap)
|
|
return -ENOMEM;
|
|
for (i = 0; i < ctx->nbuf_cap; i++) {
|
|
ctx->vbinfo_cap[i].fd = -1;
|
|
ctx->vbinfo_cap[i].comp_ratio.numer = 1;
|
|
ctx->vbinfo_cap[i].comp_ratio.denom = 1;
|
|
}
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_buf_queue - vb2_ops buf_queue callback.
|
|
* @vb: Pointer to vb2 buffer struct.
|
|
*/
|
|
static void sde_rotator_buf_queue(struct vb2_buffer *vb)
|
|
{
|
|
struct sde_rotator_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
|
|
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
|
|
|
v4l2_m2m_buf_queue(ctx->fh.m2m_ctx, vbuf);
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_buf_finish - vb2_ops buf_finish to finalize buffer before going
|
|
* back to user space
|
|
* @vb: Pointer to vb2 buffer struct.
|
|
*/
|
|
static void sde_rotator_buf_finish(struct vb2_buffer *vb)
|
|
{
|
|
struct sde_rotator_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
|
|
int i;
|
|
|
|
SDEDEV_DBG(ctx->rot_dev->dev,
|
|
"buf_finish t:%d i:%d s:%d m:%u np:%d up:%lu\n",
|
|
vb->type, vb->index, vb->state,
|
|
vb->vb2_queue->memory,
|
|
vb->num_planes,
|
|
vb->planes[0].m.userptr);
|
|
|
|
if (vb->vb2_queue->memory != VB2_MEMORY_USERPTR)
|
|
return;
|
|
|
|
/*
|
|
* We use userptr to tunnel fd, and fd can be the same across qbuf
|
|
* even though the underlying buffer is different. Since vb2 layer
|
|
* optimizes memory mapping for userptr by first checking if userptr
|
|
* has changed, it will not trigger put_userptr if fd value does
|
|
* not change. In order to force buffer release, we need to clear
|
|
* userptr when the current buffer is done and ready to go back to
|
|
* user mode. Since 0 is a valid fd, reset userptr to -1 instead.
|
|
*/
|
|
for (i = 0; i < vb->num_planes; i++)
|
|
vb->planes[i].m.userptr = ~0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_return_all_buffers - Return all buffers with the given status.
|
|
* @q: Pointer to vb2 buffer queue struct.
|
|
* @state: State of the buffer
|
|
*/
|
|
static void sde_rotator_return_all_buffers(struct vb2_queue *q,
|
|
enum vb2_buffer_state state)
|
|
{
|
|
struct sde_rotator_ctx *ctx = vb2_get_drv_priv(q);
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"return q t:%d c:%d dc:%d s:%d\n",
|
|
q->type, q->queued_count,
|
|
atomic_read(&q->owned_by_drv_count),
|
|
state);
|
|
|
|
/* return buffers according videobuffer2-core.h */
|
|
if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
|
|
struct vb2_v4l2_buffer *buf;
|
|
|
|
while ((buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx))) {
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"return vb t:%d i:%d\n",
|
|
buf->vb2_buf.type,
|
|
buf->vb2_buf.index);
|
|
v4l2_m2m_buf_done(buf, state);
|
|
}
|
|
} else if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
|
|
struct vb2_v4l2_buffer *buf;
|
|
|
|
while ((buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx))) {
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"return vb t:%d i:%d\n",
|
|
buf->vb2_buf.type,
|
|
buf->vb2_buf.index);
|
|
v4l2_m2m_buf_done(buf, state);
|
|
}
|
|
} else {
|
|
SDEDEV_ERR(rot_dev->dev, "unsupported vb t:%d\n", q->type);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_start_streaming - vb2_ops start_streaming callback.
|
|
* @q: Pointer to vb2 queue struct.
|
|
* @count: Number of buffer queued before stream on call.
|
|
*/
|
|
static int sde_rotator_start_streaming(struct vb2_queue *q, unsigned int count)
|
|
{
|
|
struct sde_rotator_ctx *ctx = vb2_get_drv_priv(q);
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
|
|
SDEDEV_DBG(rot_dev->dev, "start streaming s:%d t:%d\n",
|
|
ctx->session_id, q->type);
|
|
|
|
if (!list_empty(&ctx->pending_list)) {
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"command pending error s:%d t:%d p:%d\n",
|
|
ctx->session_id, q->type,
|
|
!list_empty(&ctx->pending_list));
|
|
return -EINVAL;
|
|
}
|
|
|
|
ctx->abort_pending = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_stop_streaming - vb2_ops stop_streaming callback.
|
|
* @q: Pointer to vb2 queue struct.
|
|
*
|
|
* This function will block waiting for stream to stop. Unlock queue
|
|
* lock to avoid deadlock.
|
|
*/
|
|
static void sde_rotator_stop_streaming(struct vb2_queue *q)
|
|
{
|
|
struct sde_rotator_ctx *ctx = vb2_get_drv_priv(q);
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
struct sde_rotator_request *request;
|
|
struct list_head *curr, *next;
|
|
int i;
|
|
int ret;
|
|
|
|
SDEDEV_DBG(rot_dev->dev, "stop streaming s:%d t:%d p:%d\n",
|
|
ctx->session_id, q->type,
|
|
!list_empty(&ctx->pending_list));
|
|
ctx->abort_pending = 1;
|
|
mutex_unlock(q->lock);
|
|
ret = wait_event_timeout(ctx->wait_queue,
|
|
list_empty(&ctx->pending_list),
|
|
msecs_to_jiffies(rot_dev->streamoff_timeout));
|
|
mutex_lock(q->lock);
|
|
if (!ret) {
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"timeout to stream off s:%d t:%d p:%d\n",
|
|
ctx->session_id, q->type,
|
|
!list_empty(&ctx->pending_list));
|
|
SDEROT_EVTLOG(ctx->session_id, q->type,
|
|
!list_empty(&ctx->pending_list),
|
|
SDE_ROT_EVTLOG_ERROR);
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
sde_rotator_cancel_all_requests(rot_dev->mgr, ctx->private);
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
list_for_each_safe(curr, next, &ctx->pending_list) {
|
|
request = container_of(curr, struct sde_rotator_request,
|
|
list);
|
|
|
|
SDEDEV_DBG(rot_dev->dev, "cancel request s:%d\n",
|
|
ctx->session_id);
|
|
mutex_unlock(q->lock);
|
|
kthread_cancel_work_sync(&request->submit_work);
|
|
kthread_cancel_work_sync(&request->retire_work);
|
|
mutex_lock(q->lock);
|
|
spin_lock(&ctx->list_lock);
|
|
list_del_init(&request->list);
|
|
list_add_tail(&request->list, &ctx->retired_list);
|
|
spin_unlock(&ctx->list_lock);
|
|
}
|
|
}
|
|
|
|
sde_rotator_return_all_buffers(q, VB2_BUF_STATE_ERROR);
|
|
|
|
/* clear fence for buffer */
|
|
sde_rotator_resync_timeline(ctx->work_queue.timeline);
|
|
if (q->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
|
|
for (i = 0; i < ctx->nbuf_cap; i++) {
|
|
struct sde_rotator_vbinfo *vbinfo =
|
|
&ctx->vbinfo_cap[i];
|
|
|
|
if (vbinfo->fence) {
|
|
/* fence is not used */
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"put fence s:%d t:%d i:%d\n",
|
|
ctx->session_id, q->type, i);
|
|
sde_rotator_put_sync_fence(vbinfo->fence);
|
|
}
|
|
vbinfo->fence = NULL;
|
|
vbinfo->fd = -1;
|
|
}
|
|
} else if (q->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
|
|
for (i = 0; i < ctx->nbuf_out; i++) {
|
|
struct sde_rotator_vbinfo *vbinfo =
|
|
&ctx->vbinfo_out[i];
|
|
|
|
if (vbinfo->fence) {
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"put fence s:%d t:%d i:%d\n",
|
|
ctx->session_id, q->type, i);
|
|
sde_rotator_put_sync_fence(vbinfo->fence);
|
|
}
|
|
vbinfo->fence = NULL;
|
|
vbinfo->fd = -1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Videobuf2 queue callbacks. */
|
|
static const struct vb2_ops sde_rotator_vb2_q_ops = {
|
|
.queue_setup = sde_rotator_queue_setup,
|
|
.buf_queue = sde_rotator_buf_queue,
|
|
.start_streaming = sde_rotator_start_streaming,
|
|
.stop_streaming = sde_rotator_stop_streaming,
|
|
.wait_prepare = vb2_ops_wait_prepare,
|
|
.wait_finish = vb2_ops_wait_finish,
|
|
.buf_finish = sde_rotator_buf_finish,
|
|
};
|
|
|
|
/*
|
|
* sde_rotator_get_userptr - Map and get buffer handler for user pointer buffer.
|
|
* @vb: video buffer
|
|
* @dev: device allocated in buf_setup.
|
|
* @vaddr: Virtual addr passed from userpsace (in our case ion fd)
|
|
* @size: Size of the buffer
|
|
*/
|
|
static void *sde_rotator_get_userptr(struct vb2_buffer *vb, struct device *dev,
|
|
unsigned long vaddr, unsigned long size)
|
|
{
|
|
struct sde_rotator_ctx *ctx = (struct sde_rotator_ctx *)dev;
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
struct sde_rotator_buf_handle *buf;
|
|
|
|
buf = kzalloc(sizeof(*buf), GFP_KERNEL);
|
|
if (!buf)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
buf->fd = vaddr;
|
|
buf->secure = ctx->secure || ctx->secure_camera;
|
|
buf->ctx = ctx;
|
|
buf->rot_dev = rot_dev;
|
|
buf->size = size;
|
|
buf->buffer = dma_buf_get(buf->fd);
|
|
if (IS_ERR_OR_NULL(buf->buffer)) {
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"fail get dmabuf fd:%d r:%ld\n",
|
|
buf->fd, PTR_ERR(buf->buffer));
|
|
goto error_buf_get;
|
|
}
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"get dmabuf s:%d fd:%d buf:%pad\n",
|
|
buf->ctx->session_id,
|
|
buf->fd, &buf->buffer);
|
|
|
|
return buf;
|
|
error_buf_get:
|
|
kfree(buf);
|
|
return ERR_PTR(-ENOMEM);
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_put_userptr - Unmap and free buffer handler.
|
|
* @buf_priv: Buffer handler allocated get_userptr callback.
|
|
*/
|
|
static void sde_rotator_put_userptr(void *buf_priv)
|
|
{
|
|
struct sde_rotator_buf_handle *buf = buf_priv;
|
|
|
|
if (IS_ERR_OR_NULL(buf))
|
|
return;
|
|
|
|
if (!buf->rot_dev || !buf->ctx) {
|
|
WARN_ON(!buf->rot_dev || !buf->ctx);
|
|
SDEROT_ERR("null rotator device/context\n");
|
|
return;
|
|
}
|
|
|
|
SDEDEV_DBG(buf->rot_dev->dev, "put dmabuf s:%d fd:%d buf:%pad\n",
|
|
buf->ctx->session_id,
|
|
buf->fd, &buf->buffer);
|
|
|
|
if (buf->buffer) {
|
|
dma_buf_put(buf->buffer);
|
|
buf->buffer = NULL;
|
|
}
|
|
|
|
kfree(buf_priv);
|
|
}
|
|
|
|
/* Videobuf2 memory callbacks. */
|
|
static struct vb2_mem_ops sde_rotator_vb2_mem_ops = {
|
|
.get_userptr = sde_rotator_get_userptr,
|
|
.put_userptr = sde_rotator_put_userptr,
|
|
};
|
|
|
|
/*
|
|
* sde_rotator_s_ctx_ctrl - set context control variable to v4l2 control
|
|
* @ctx: Pointer to rotator context.
|
|
* @ctx_ctrl: Pointer to context control variable
|
|
* @ctrl: Pointer to v4l2 control variable
|
|
*/
|
|
static int sde_rotator_s_ctx_ctrl(struct sde_rotator_ctx *ctx,
|
|
s32 *ctx_ctrl, struct v4l2_ctrl *ctrl)
|
|
{
|
|
*ctx_ctrl = ctrl->val;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_s_ctrl - Set control.
|
|
* @ctrl: Pointer to v4l2 control structure.
|
|
*/
|
|
static int sde_rotator_s_ctrl(struct v4l2_ctrl *ctrl)
|
|
{
|
|
struct sde_rotator_ctx *ctx =
|
|
container_of(ctrl->handler,
|
|
struct sde_rotator_ctx, ctrl_handler);
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
int ret;
|
|
|
|
SDEDEV_DBG(rot_dev->dev, "set %s:%d s:%d\n", ctrl->name, ctrl->val,
|
|
ctx->session_id);
|
|
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
|
|
switch (ctrl->id) {
|
|
case V4L2_CID_HFLIP:
|
|
ret = sde_rotator_s_ctx_ctrl(ctx, &ctx->hflip, ctrl);
|
|
break;
|
|
|
|
case V4L2_CID_VFLIP:
|
|
ret = sde_rotator_s_ctx_ctrl(ctx, &ctx->vflip, ctrl);
|
|
break;
|
|
|
|
case V4L2_CID_ROTATE:
|
|
ret = sde_rotator_s_ctx_ctrl(ctx, &ctx->rotate, ctrl);
|
|
break;
|
|
|
|
case V4L2_CID_SDE_ROTATOR_SECURE:
|
|
ret = sde_rotator_s_ctx_ctrl(ctx, &ctx->secure, ctrl);
|
|
break;
|
|
|
|
case V4L2_CID_SDE_ROTATOR_SECURE_CAMERA:
|
|
ret = sde_rotator_s_ctx_ctrl(ctx, &ctx->secure_camera, ctrl);
|
|
break;
|
|
default:
|
|
v4l2_warn(&rot_dev->v4l2_dev, "invalid control %d\n", ctrl->id);
|
|
ret = -EINVAL;
|
|
}
|
|
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_ctrl_ops - Control operations.
|
|
*/
|
|
static const struct v4l2_ctrl_ops sde_rotator_ctrl_ops = {
|
|
.s_ctrl = sde_rotator_s_ctrl,
|
|
};
|
|
|
|
/*
|
|
* sde_rotator_ctrl_secure - Non-secure/Secure.
|
|
*/
|
|
static const struct v4l2_ctrl_config sde_rotator_ctrl_secure = {
|
|
.ops = &sde_rotator_ctrl_ops,
|
|
.id = V4L2_CID_SDE_ROTATOR_SECURE,
|
|
.name = "Non-secure/Secure Domain",
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.def = 0,
|
|
.min = 0,
|
|
.max = 1,
|
|
.step = 1,
|
|
};
|
|
|
|
static const struct v4l2_ctrl_config sde_rotator_ctrl_secure_camera = {
|
|
.ops = &sde_rotator_ctrl_ops,
|
|
.id = V4L2_CID_SDE_ROTATOR_SECURE_CAMERA,
|
|
.name = "Secure Camera content",
|
|
.type = V4L2_CTRL_TYPE_INTEGER,
|
|
.def = 0,
|
|
.min = 0,
|
|
.max = 1,
|
|
.step = 1,
|
|
};
|
|
|
|
/*
|
|
* sde_rotator_ctx_show - show context state.
|
|
*/
|
|
static ssize_t sde_rotator_ctx_show(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
size_t len = PAGE_SIZE;
|
|
int cnt = 0;
|
|
struct sde_rotator_ctx *ctx =
|
|
container_of(kobj, struct sde_rotator_ctx, kobj);
|
|
|
|
if (!ctx)
|
|
return cnt;
|
|
|
|
#define SPRINT(fmt, ...) \
|
|
(cnt += scnprintf(buf + cnt, len - cnt, fmt, ##__VA_ARGS__))
|
|
|
|
SPRINT("rotate=%d\n", ctx->rotate);
|
|
SPRINT("hflip=%d\n", ctx->hflip);
|
|
SPRINT("vflip=%d\n", ctx->vflip);
|
|
SPRINT("priority=%d\n", ctx->fh.prio);
|
|
SPRINT("secure=%d\n", ctx->secure);
|
|
SPRINT("timeperframe=%u %u\n", ctx->timeperframe.numerator,
|
|
ctx->timeperframe.denominator);
|
|
SPRINT("nbuf_out=%d\n", ctx->nbuf_out);
|
|
SPRINT("nbuf_cap=%d\n", ctx->nbuf_cap);
|
|
SPRINT("crop_out=%u %u %u %u\n",
|
|
ctx->crop_out.left, ctx->crop_out.top,
|
|
ctx->crop_out.width, ctx->crop_out.height);
|
|
SPRINT("crop_cap=%u %u %u %u\n",
|
|
ctx->crop_cap.left, ctx->crop_cap.top,
|
|
ctx->crop_cap.width, ctx->crop_cap.height);
|
|
SPRINT("fmt_out=%c%c%c%c %u %u %u %u\n",
|
|
(ctx->format_out.fmt.pix.pixelformat>>0)&0xff,
|
|
(ctx->format_out.fmt.pix.pixelformat>>8)&0xff,
|
|
(ctx->format_out.fmt.pix.pixelformat>>16)&0xff,
|
|
(ctx->format_out.fmt.pix.pixelformat>>24)&0xff,
|
|
ctx->format_out.fmt.pix.width,
|
|
ctx->format_out.fmt.pix.height,
|
|
ctx->format_out.fmt.pix.bytesperline,
|
|
ctx->format_out.fmt.pix.sizeimage);
|
|
SPRINT("fmt_cap=%c%c%c%c %u %u %u %u\n",
|
|
(ctx->format_cap.fmt.pix.pixelformat>>0)&0xff,
|
|
(ctx->format_cap.fmt.pix.pixelformat>>8)&0xff,
|
|
(ctx->format_cap.fmt.pix.pixelformat>>16)&0xff,
|
|
(ctx->format_cap.fmt.pix.pixelformat>>24)&0xff,
|
|
ctx->format_cap.fmt.pix.width,
|
|
ctx->format_cap.fmt.pix.height,
|
|
ctx->format_cap.fmt.pix.bytesperline,
|
|
ctx->format_cap.fmt.pix.sizeimage);
|
|
SPRINT("abort_pending=%d\n", ctx->abort_pending);
|
|
SPRINT("command_pending=%d\n", !list_empty(&ctx->pending_list));
|
|
SPRINT("sequence=%u\n",
|
|
sde_rotator_get_timeline_commit_ts(ctx->work_queue.timeline));
|
|
SPRINT("timestamp=%u\n",
|
|
sde_rotator_get_timeline_retire_ts(ctx->work_queue.timeline));
|
|
return cnt;
|
|
}
|
|
|
|
static struct kobj_attribute sde_rotator_ctx_attr =
|
|
__ATTR(state, 0664, sde_rotator_ctx_show, NULL);
|
|
|
|
static struct attribute *sde_rotator_fs_attrs[] = {
|
|
&sde_rotator_ctx_attr.attr,
|
|
NULL
|
|
};
|
|
|
|
static struct attribute_group sde_rotator_fs_attr_group = {
|
|
.attrs = sde_rotator_fs_attrs
|
|
};
|
|
|
|
/*
|
|
* sde_rotator_ctx_show - sysfs show callback.
|
|
*/
|
|
static ssize_t sde_rotator_fs_show(struct kobject *kobj,
|
|
struct attribute *attr, char *buf)
|
|
{
|
|
ssize_t ret = -EIO;
|
|
struct kobj_attribute *kattr =
|
|
container_of(attr, struct kobj_attribute, attr);
|
|
if (kattr->show)
|
|
ret = kattr->show(kobj, kattr, buf);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_fs_store - sysfs store callback.
|
|
*/
|
|
static ssize_t sde_rotator_fs_store(struct kobject *kobj,
|
|
struct attribute *attr, const char *buf, size_t count)
|
|
{
|
|
ssize_t ret = -EIO;
|
|
struct kobj_attribute *kattr =
|
|
container_of(attr, struct kobj_attribute, attr);
|
|
if (kattr->store)
|
|
ret = kattr->store(kobj, kattr, buf, count);
|
|
return ret;
|
|
}
|
|
|
|
static const struct sysfs_ops sde_rotator_fs_ops = {
|
|
.show = sde_rotator_fs_show,
|
|
.store = sde_rotator_fs_store,
|
|
};
|
|
|
|
static struct kobj_type sde_rotator_fs_ktype = {
|
|
.sysfs_ops = &sde_rotator_fs_ops,
|
|
};
|
|
|
|
/*
|
|
* sde_rotator_queue_init - m2m_ops queue_setup callback.
|
|
* @priv: Pointer to rotator ctx.
|
|
* @src_vq: vb2 source queue.
|
|
* @dst_vq: vb2 destination queue.
|
|
*/
|
|
static int sde_rotator_queue_init(void *priv, struct vb2_queue *src_vq,
|
|
struct vb2_queue *dst_vq)
|
|
{
|
|
struct sde_rotator_ctx *ctx = priv;
|
|
int ret;
|
|
|
|
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
|
|
src_vq->io_modes = VB2_USERPTR;
|
|
src_vq->drv_priv = ctx;
|
|
src_vq->mem_ops = &sde_rotator_vb2_mem_ops;
|
|
src_vq->ops = &sde_rotator_vb2_q_ops;
|
|
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
|
|
src_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
|
|
src_vq->lock = &ctx->rot_dev->lock;
|
|
src_vq->min_buffers_needed = 1;
|
|
src_vq->dev = ctx->rot_dev->dev;
|
|
|
|
ret = vb2_queue_init(src_vq);
|
|
if (ret) {
|
|
SDEDEV_ERR(ctx->rot_dev->dev,
|
|
"fail init src queue r:%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
dst_vq->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
dst_vq->io_modes = VB2_USERPTR;
|
|
dst_vq->drv_priv = ctx;
|
|
dst_vq->mem_ops = &sde_rotator_vb2_mem_ops;
|
|
dst_vq->ops = &sde_rotator_vb2_q_ops;
|
|
dst_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
|
|
dst_vq->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY;
|
|
dst_vq->lock = &ctx->rot_dev->lock;
|
|
dst_vq->min_buffers_needed = 1;
|
|
src_vq->dev = ctx->rot_dev->dev;
|
|
|
|
ret = vb2_queue_init(dst_vq);
|
|
if (ret) {
|
|
SDEDEV_ERR(ctx->rot_dev->dev,
|
|
"fail init dst queue r:%d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_ctx_open - Rotator device open method.
|
|
* @rot_dev: Pointer to rotator device structure
|
|
* @file: Pointer to file struct (optional)
|
|
* return: Pointer rotator context if success; ptr error code, otherwise.
|
|
*/
|
|
struct sde_rotator_ctx *sde_rotator_ctx_open(
|
|
struct sde_rotator_device *rot_dev, struct file *file)
|
|
{
|
|
struct video_device *video = file ? video_devdata(file) : NULL;
|
|
struct sde_rotator_ctx *ctx;
|
|
struct v4l2_ctrl_handler *ctrl_handler;
|
|
char name[32];
|
|
int i, ret;
|
|
|
|
if (atomic_read(&rot_dev->mgr->device_suspended))
|
|
return ERR_PTR(-EPERM);
|
|
|
|
ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
|
|
if (!ctx)
|
|
return ERR_PTR(-ENOMEM);
|
|
|
|
if (mutex_lock_interruptible(&rot_dev->lock)) {
|
|
ret = -ERESTARTSYS;
|
|
goto error_lock;
|
|
}
|
|
|
|
/* wait until exclusive ctx, if exists, finishes or timeout */
|
|
while (rot_dev->excl_ctx) {
|
|
SDEROT_DBG("waiting to open %s session %d ...\n",
|
|
file ? "v4l2" : "excl", rot_dev->session_id);
|
|
mutex_unlock(&rot_dev->lock);
|
|
ret = wait_event_interruptible_timeout(rot_dev->open_wq,
|
|
!rot_dev->excl_ctx,
|
|
msecs_to_jiffies(rot_dev->open_timeout));
|
|
if (ret < 0) {
|
|
goto error_lock;
|
|
} else if (!ret) {
|
|
SDEROT_WARN("timeout to open session %d\n",
|
|
rot_dev->session_id);
|
|
SDEROT_EVTLOG(rot_dev->session_id,
|
|
SDE_ROT_EVTLOG_ERROR);
|
|
ret = -EBUSY;
|
|
goto error_lock;
|
|
} else if (mutex_lock_interruptible(&rot_dev->lock)) {
|
|
ret = -ERESTARTSYS;
|
|
goto error_lock;
|
|
}
|
|
}
|
|
|
|
ctx->rot_dev = rot_dev;
|
|
ctx->file = file;
|
|
|
|
/* Set context defaults */
|
|
ctx->session_id = rot_dev->session_id++;
|
|
SDEDEV_DBG(ctx->rot_dev->dev, "open %d\n", ctx->session_id);
|
|
ctx->timeperframe.numerator = 1;
|
|
ctx->timeperframe.denominator = SDE_ROTATOR_DEFAULT_FPS;
|
|
ctx->hflip = 0;
|
|
ctx->vflip = 0;
|
|
ctx->rotate = 0;
|
|
ctx->secure = 0;
|
|
ctx->abort_pending = 0;
|
|
ctx->kthread_id = -1;
|
|
ctx->format_cap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
ctx->format_cap.fmt.pix.pixelformat = SDE_PIX_FMT_Y_CBCR_H2V2;
|
|
ctx->format_cap.fmt.pix.width = 640;
|
|
ctx->format_cap.fmt.pix.height = 480;
|
|
ctx->crop_cap.width = 640;
|
|
ctx->crop_cap.height = 480;
|
|
ctx->format_out.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
|
|
ctx->format_out.fmt.pix.pixelformat = SDE_PIX_FMT_Y_CBCR_H2V2;
|
|
ctx->format_out.fmt.pix.width = 640;
|
|
ctx->format_out.fmt.pix.height = 480;
|
|
ctx->crop_out.width = 640;
|
|
ctx->crop_out.height = 480;
|
|
init_waitqueue_head(&ctx->wait_queue);
|
|
spin_lock_init(&ctx->list_lock);
|
|
INIT_LIST_HEAD(&ctx->pending_list);
|
|
INIT_LIST_HEAD(&ctx->retired_list);
|
|
|
|
for (i = 0 ; i < ARRAY_SIZE(ctx->requests); i++) {
|
|
struct sde_rotator_request *request = &ctx->requests[i];
|
|
|
|
kthread_init_work(&request->submit_work,
|
|
sde_rotator_submit_handler);
|
|
kthread_init_work(&request->retire_work,
|
|
sde_rotator_retire_handler);
|
|
request->ctx = ctx;
|
|
INIT_LIST_HEAD(&request->list);
|
|
list_add_tail(&request->list, &ctx->retired_list);
|
|
}
|
|
|
|
if (ctx->file) {
|
|
v4l2_fh_init(&ctx->fh, video);
|
|
file->private_data = &ctx->fh;
|
|
v4l2_fh_add(&ctx->fh);
|
|
|
|
ctx->fh.m2m_ctx = v4l2_m2m_ctx_init(rot_dev->m2m_dev,
|
|
ctx, sde_rotator_queue_init);
|
|
if (IS_ERR_OR_NULL(ctx->fh.m2m_ctx)) {
|
|
ret = PTR_ERR(ctx->fh.m2m_ctx);
|
|
ctx->fh.m2m_ctx = NULL;
|
|
goto error_m2m_init;
|
|
}
|
|
}
|
|
|
|
ret = kobject_init_and_add(&ctx->kobj, &sde_rotator_fs_ktype,
|
|
&rot_dev->dev->kobj, "session_%d", ctx->session_id);
|
|
if (ret) {
|
|
SDEDEV_ERR(ctx->rot_dev->dev,
|
|
"fail initialize context kobject\n");
|
|
goto error_kobj_init;
|
|
}
|
|
|
|
ret = sysfs_create_group(&ctx->kobj, &sde_rotator_fs_attr_group);
|
|
if (ret) {
|
|
SDEDEV_ERR(ctx->rot_dev->dev,
|
|
"fail register rotator sysfs nodes\n");
|
|
goto error_create_sysfs;
|
|
}
|
|
|
|
for (i = 0; i < MAX_ROT_OPEN_SESSION; i++) {
|
|
if (rot_dev->kthread_free[i]) {
|
|
rot_dev->kthread_free[i] = false;
|
|
ctx->kthread_id = i;
|
|
ctx->work_queue.rot_kw = &rot_dev->rot_kw[i];
|
|
ctx->work_queue.rot_thread = rot_dev->rot_thread[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ctx->kthread_id < 0) {
|
|
SDEDEV_ERR(ctx->rot_dev->dev,
|
|
"fail to acquire the kthread\n");
|
|
ret = -EINVAL;
|
|
goto error_alloc_kthread;
|
|
}
|
|
|
|
snprintf(name, sizeof(name), "%d_%d", rot_dev->dev->id,
|
|
ctx->session_id);
|
|
ctx->work_queue.timeline = sde_rotator_create_timeline(name);
|
|
if (!ctx->work_queue.timeline)
|
|
SDEDEV_DBG(ctx->rot_dev->dev, "timeline is not available\n");
|
|
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
ret = sde_rotator_session_open(rot_dev->mgr, &ctx->private,
|
|
ctx->session_id, &ctx->work_queue);
|
|
if (ret < 0) {
|
|
SDEDEV_ERR(ctx->rot_dev->dev, "fail open session\n");
|
|
goto error_open_session;
|
|
}
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
|
|
if (ctx->file) {
|
|
/* Create control */
|
|
ctrl_handler = &ctx->ctrl_handler;
|
|
v4l2_ctrl_handler_init(ctrl_handler, 4);
|
|
v4l2_ctrl_new_std(ctrl_handler,
|
|
&sde_rotator_ctrl_ops, V4L2_CID_HFLIP, 0, 1, 1, 0);
|
|
v4l2_ctrl_new_std(ctrl_handler,
|
|
&sde_rotator_ctrl_ops, V4L2_CID_VFLIP, 0, 1, 1, 0);
|
|
v4l2_ctrl_new_std(ctrl_handler,
|
|
&sde_rotator_ctrl_ops, V4L2_CID_ROTATE, 0, 270, 90, 0);
|
|
v4l2_ctrl_new_custom(ctrl_handler,
|
|
&sde_rotator_ctrl_secure, NULL);
|
|
v4l2_ctrl_new_custom(ctrl_handler,
|
|
&sde_rotator_ctrl_secure_camera, NULL);
|
|
if (ctrl_handler->error) {
|
|
ret = ctrl_handler->error;
|
|
v4l2_ctrl_handler_free(ctrl_handler);
|
|
goto error_ctrl_handler;
|
|
}
|
|
ctx->fh.ctrl_handler = ctrl_handler;
|
|
v4l2_ctrl_handler_setup(ctrl_handler);
|
|
} else {
|
|
/* acquire exclusive context */
|
|
SDEDEV_DBG(rot_dev->dev, "acquire exclusive session id:%u\n",
|
|
ctx->session_id);
|
|
SDEROT_EVTLOG(ctx->session_id);
|
|
rot_dev->excl_ctx = ctx;
|
|
}
|
|
|
|
mutex_unlock(&rot_dev->lock);
|
|
|
|
SDEDEV_DBG(ctx->rot_dev->dev, "SDE v4l2 rotator open success\n");
|
|
|
|
ATRACE_BEGIN(ctx->kobj.name);
|
|
|
|
return ctx;
|
|
error_ctrl_handler:
|
|
error_open_session:
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
sde_rotator_destroy_timeline(ctx->work_queue.timeline);
|
|
rot_dev->kthread_free[ctx->kthread_id] = true;
|
|
ctx->kthread_id = -1;
|
|
error_alloc_kthread:
|
|
sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group);
|
|
error_create_sysfs:
|
|
kobject_put(&ctx->kobj);
|
|
error_kobj_init:
|
|
if (ctx->file) {
|
|
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
|
|
ctx->fh.m2m_ctx = NULL;
|
|
}
|
|
error_m2m_init:
|
|
if (ctx->file) {
|
|
v4l2_fh_del(&ctx->fh);
|
|
v4l2_fh_exit(&ctx->fh);
|
|
}
|
|
mutex_unlock(&rot_dev->lock);
|
|
error_lock:
|
|
kfree(ctx);
|
|
ctx = NULL;
|
|
return ERR_PTR(ret);
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_ctx_release - Rotator device release method.
|
|
* @ctx: Pointer rotator context.
|
|
* @file: Pointer to file struct (optional)
|
|
* return: 0 if success; error code, otherwise
|
|
*/
|
|
static int sde_rotator_ctx_release(struct sde_rotator_ctx *ctx,
|
|
struct file *file)
|
|
{
|
|
struct sde_rotator_device *rot_dev;
|
|
u32 session_id;
|
|
struct list_head *curr, *next;
|
|
|
|
if (!ctx) {
|
|
SDEROT_DBG("ctx is NULL\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rot_dev = ctx->rot_dev;
|
|
session_id = ctx->session_id;
|
|
|
|
ATRACE_END(ctx->kobj.name);
|
|
|
|
SDEDEV_DBG(rot_dev->dev, "release s:%d\n", session_id);
|
|
mutex_lock(&rot_dev->lock);
|
|
if (rot_dev->excl_ctx == ctx) {
|
|
SDEDEV_DBG(rot_dev->dev, "release exclusive session id:%u\n",
|
|
session_id);
|
|
SDEROT_EVTLOG(session_id);
|
|
rot_dev->excl_ctx = NULL;
|
|
}
|
|
if (ctx->file) {
|
|
v4l2_ctrl_handler_free(&ctx->ctrl_handler);
|
|
SDEDEV_DBG(rot_dev->dev, "release streams s:%d\n", session_id);
|
|
if (ctx->fh.m2m_ctx) {
|
|
v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx,
|
|
V4L2_BUF_TYPE_VIDEO_OUTPUT);
|
|
v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx,
|
|
V4L2_BUF_TYPE_VIDEO_CAPTURE);
|
|
}
|
|
}
|
|
mutex_unlock(&rot_dev->lock);
|
|
SDEDEV_DBG(rot_dev->dev, "release submit work s:%d\n", session_id);
|
|
list_for_each_safe(curr, next, &ctx->pending_list) {
|
|
struct sde_rotator_request *request =
|
|
container_of(curr, struct sde_rotator_request, list);
|
|
|
|
SDEDEV_DBG(rot_dev->dev, "release submit work s:%d\n",
|
|
session_id);
|
|
kthread_cancel_work_sync(&request->submit_work);
|
|
}
|
|
SDEDEV_DBG(rot_dev->dev, "release session s:%d\n", session_id);
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
sde_rotator_session_close(rot_dev->mgr, ctx->private, session_id);
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
SDEDEV_DBG(rot_dev->dev, "release retire work s:%d\n", session_id);
|
|
list_for_each_safe(curr, next, &ctx->pending_list) {
|
|
struct sde_rotator_request *request =
|
|
container_of(curr, struct sde_rotator_request, list);
|
|
|
|
SDEDEV_DBG(rot_dev->dev, "release retire work s:%d\n",
|
|
session_id);
|
|
kthread_cancel_work_sync(&request->retire_work);
|
|
}
|
|
mutex_lock(&rot_dev->lock);
|
|
SDEDEV_DBG(rot_dev->dev, "release context s:%d\n", session_id);
|
|
sde_rotator_destroy_timeline(ctx->work_queue.timeline);
|
|
if (ctx->kthread_id >= 0 && ctx->work_queue.rot_kw) {
|
|
kthread_flush_worker(ctx->work_queue.rot_kw);
|
|
rot_dev->kthread_free[ctx->kthread_id] = true;
|
|
}
|
|
sysfs_remove_group(&ctx->kobj, &sde_rotator_fs_attr_group);
|
|
kobject_put(&ctx->kobj);
|
|
if (ctx->file) {
|
|
if (ctx->fh.m2m_ctx)
|
|
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
|
|
if (ctx->fh.vdev) {
|
|
v4l2_fh_del(&ctx->fh);
|
|
v4l2_fh_exit(&ctx->fh);
|
|
}
|
|
}
|
|
kfree(ctx->vbinfo_out);
|
|
kfree(ctx->vbinfo_cap);
|
|
kfree(ctx);
|
|
wake_up_interruptible(&rot_dev->open_wq);
|
|
mutex_unlock(&rot_dev->lock);
|
|
SDEDEV_DBG(rot_dev->dev, "release complete s:%d\n", session_id);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_update_retire_sequence - update retired sequence of the context
|
|
* referenced in the request, and wake up any waiting for update event
|
|
* @request: Pointer to rotator request
|
|
*/
|
|
static void sde_rotator_update_retire_sequence(
|
|
struct sde_rotator_request *request)
|
|
{
|
|
struct sde_rotator_ctx *ctx;
|
|
|
|
if (!request || !request->ctx) {
|
|
SDEROT_ERR("invalid parameters\n");
|
|
return;
|
|
}
|
|
|
|
ctx = request->ctx;
|
|
ctx->retired_sequence_id = request->sequence_id;
|
|
|
|
wake_up(&ctx->wait_queue);
|
|
|
|
SDEROT_DBG("update sequence s:%d.%d\n",
|
|
ctx->session_id, ctx->retired_sequence_id);
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_retire_request - retire the given rotator request with
|
|
* device mutex locked
|
|
* @request: Pointer to rotator request
|
|
*/
|
|
static void sde_rotator_retire_request(struct sde_rotator_request *request)
|
|
{
|
|
struct sde_rotator_ctx *ctx;
|
|
|
|
if (!request || !request->ctx) {
|
|
SDEROT_ERR("invalid parameters\n");
|
|
return;
|
|
}
|
|
|
|
ctx = request->ctx;
|
|
|
|
request->req = NULL;
|
|
request->sequence_id = 0;
|
|
request->committed = false;
|
|
spin_lock(&ctx->list_lock);
|
|
list_del_init(&request->list);
|
|
list_add_tail(&request->list, &ctx->retired_list);
|
|
spin_unlock(&ctx->list_lock);
|
|
|
|
wake_up(&ctx->wait_queue);
|
|
|
|
SDEROT_DBG("retire request s:%d.%d\n",
|
|
ctx->session_id, ctx->retired_sequence_id);
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_is_request_retired - Return true if given request already expired
|
|
* @request: Pointer to rotator request
|
|
*/
|
|
static bool sde_rotator_is_request_retired(struct sde_rotator_request *request)
|
|
{
|
|
struct sde_rotator_ctx *ctx;
|
|
u32 sequence_id;
|
|
s32 retire_delta;
|
|
|
|
if (!request || !request->ctx)
|
|
return true;
|
|
|
|
ctx = request->ctx;
|
|
sequence_id = request->sequence_id;
|
|
|
|
retire_delta = (s32) (ctx->retired_sequence_id - sequence_id);
|
|
|
|
SDEROT_DBG("sequence:%u/%u\n", sequence_id, ctx->retired_sequence_id);
|
|
|
|
return retire_delta >= 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_inline_open - open inline rotator session
|
|
* @pdev: Pointer to rotator platform device
|
|
* @video_mode: true if video mode is requested
|
|
* return: Pointer to new rotator session context
|
|
*/
|
|
void *sde_rotator_inline_open(struct platform_device *pdev)
|
|
{
|
|
struct sde_rotator_device *rot_dev;
|
|
struct sde_rotator_ctx *ctx;
|
|
int rc;
|
|
|
|
if (!pdev) {
|
|
SDEROT_ERR("invalid platform device\n");
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
rot_dev = (struct sde_rotator_device *) platform_get_drvdata(pdev);
|
|
if (!rot_dev) {
|
|
SDEROT_ERR("invalid rotator device\n");
|
|
return ERR_PTR(-EINVAL);
|
|
}
|
|
|
|
ctx = sde_rotator_ctx_open(rot_dev, NULL);
|
|
if (IS_ERR_OR_NULL(ctx)) {
|
|
rc = PTR_ERR(ctx);
|
|
SDEROT_ERR("failed to open rotator context %d\n", rc);
|
|
goto rotator_open_error;
|
|
}
|
|
|
|
ctx->slice = llcc_slice_getd(LLCC_ROTATOR);
|
|
if (IS_ERR(ctx->slice)) {
|
|
rc = PTR_ERR(ctx->slice);
|
|
SDEROT_ERR("failed to get system cache %d\n", rc);
|
|
goto slice_getd_error;
|
|
}
|
|
|
|
if (!rot_dev->disable_syscache) {
|
|
rc = llcc_slice_activate(ctx->slice);
|
|
if (rc) {
|
|
SDEROT_ERR("failed to activate slice %d\n", rc);
|
|
goto activate_error;
|
|
}
|
|
SDEROT_DBG("scid %d size %zukb\n",
|
|
llcc_get_slice_id(ctx->slice),
|
|
llcc_get_slice_size(ctx->slice));
|
|
} else {
|
|
SDEROT_DBG("syscache bypassed\n");
|
|
}
|
|
|
|
SDEROT_EVTLOG(ctx->session_id, llcc_get_slice_id(ctx->slice),
|
|
llcc_get_slice_size(ctx->slice),
|
|
rot_dev->disable_syscache);
|
|
|
|
return ctx;
|
|
|
|
activate_error:
|
|
llcc_slice_putd(ctx->slice);
|
|
ctx->slice = NULL;
|
|
slice_getd_error:
|
|
sde_rotator_ctx_release(ctx, NULL);
|
|
rotator_open_error:
|
|
return ERR_PTR(rc);
|
|
}
|
|
EXPORT_SYMBOL(sde_rotator_inline_open);
|
|
|
|
int sde_rotator_inline_release(void *handle)
|
|
{
|
|
struct sde_rotator_device *rot_dev;
|
|
struct sde_rotator_ctx *ctx;
|
|
|
|
if (!handle) {
|
|
SDEROT_ERR("invalid rotator ctx\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ctx = handle;
|
|
rot_dev = ctx->rot_dev;
|
|
|
|
if (!rot_dev) {
|
|
SDEROT_ERR("invalid rotator device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (ctx->slice) {
|
|
if (!rot_dev->disable_syscache)
|
|
llcc_slice_deactivate(ctx->slice);
|
|
llcc_slice_putd(ctx->slice);
|
|
ctx->slice = NULL;
|
|
}
|
|
|
|
SDEROT_EVTLOG(ctx->session_id);
|
|
|
|
return sde_rotator_ctx_release(ctx, NULL);
|
|
}
|
|
EXPORT_SYMBOL(sde_rotator_inline_release);
|
|
|
|
/*
|
|
* sde_rotator_inline_get_dst_pixfmt - determine output pixel format
|
|
* @pdev: Pointer to platform device
|
|
* @src_pixfmt: input pixel format
|
|
* @dst_pixfmt: Pointer to output pixel format (output)
|
|
* return: 0 if success; error code otherwise
|
|
*/
|
|
int sde_rotator_inline_get_dst_pixfmt(struct platform_device *pdev,
|
|
u32 src_pixfmt, u32 *dst_pixfmt)
|
|
{
|
|
int rc;
|
|
|
|
if (!src_pixfmt || !dst_pixfmt)
|
|
return -EINVAL;
|
|
|
|
rc = sde_rot_get_base_tilea5x_pixfmt(src_pixfmt, dst_pixfmt);
|
|
if (rc)
|
|
return rc;
|
|
|
|
/*
|
|
* Currently, NV21 tile is not supported as output; hence,
|
|
* override with NV12 tile.
|
|
*/
|
|
if (*dst_pixfmt == SDE_PIX_FMT_Y_CRCB_H2V2_TILE)
|
|
*dst_pixfmt = SDE_PIX_FMT_Y_CBCR_H2V2_TILE;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(sde_rotator_inline_get_dst_pixfmt);
|
|
|
|
/*
|
|
* sde_rotator_inline_get_downscale_caps - get scaling capability
|
|
* @pdev: Pointer to platform device
|
|
* @caps: string buffer for capability
|
|
* @len: length of string buffer
|
|
* return: length of capability string
|
|
*/
|
|
int sde_rotator_inline_get_downscale_caps(struct platform_device *pdev,
|
|
char *caps, int len)
|
|
{
|
|
struct sde_rotator_device *rot_dev;
|
|
int rc;
|
|
|
|
if (!pdev) {
|
|
SDEROT_ERR("invalid platform device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rot_dev = (struct sde_rotator_device *) platform_get_drvdata(pdev);
|
|
if (!rot_dev || !rot_dev->mgr) {
|
|
SDEROT_ERR("invalid rotator device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
rc = sde_rotator_get_downscale_caps(rot_dev->mgr, caps, len);
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
|
|
return rc;
|
|
}
|
|
EXPORT_SYMBOL(sde_rotator_inline_get_downscale_caps);
|
|
|
|
/*
|
|
* sde_rotator_inline_get_maxlinewidth - get maximum line width of rotator
|
|
* @pdev: Pointer to platform device
|
|
* return: maximum line width
|
|
*/
|
|
int sde_rotator_inline_get_maxlinewidth(struct platform_device *pdev)
|
|
{
|
|
struct sde_rotator_device *rot_dev;
|
|
int maxlinewidth;
|
|
|
|
if (!pdev) {
|
|
SDEROT_ERR("invalid platform device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rot_dev = (struct sde_rotator_device *)platform_get_drvdata(pdev);
|
|
if (!rot_dev || !rot_dev->mgr) {
|
|
SDEROT_ERR("invalid rotator device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
maxlinewidth = sde_rotator_get_maxlinewidth(rot_dev->mgr);
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
|
|
return maxlinewidth;
|
|
}
|
|
EXPORT_SYMBOL(sde_rotator_inline_get_maxlinewidth);
|
|
|
|
/*
|
|
* sde_rotator_inline_get_pixfmt_caps - get pixel format capability
|
|
* @pdev: Pointer to platform device
|
|
* @pixfmt: array of pixel format buffer
|
|
* @len: length of pixel format buffer
|
|
* return: length of pixel format capability if success; error code otherwise
|
|
*/
|
|
int sde_rotator_inline_get_pixfmt_caps(struct platform_device *pdev,
|
|
bool input, u32 *pixfmts, int len)
|
|
{
|
|
struct sde_rotator_device *rot_dev;
|
|
u32 i, pixfmt;
|
|
|
|
if (!pdev) {
|
|
SDEROT_ERR("invalid platform device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
rot_dev = (struct sde_rotator_device *) platform_get_drvdata(pdev);
|
|
if (!rot_dev || !rot_dev->mgr) {
|
|
SDEROT_ERR("invalid rotator device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
for (i = 0;; i++) {
|
|
pixfmt = sde_rotator_get_pixfmt(rot_dev->mgr, i, input,
|
|
SDE_ROTATOR_MODE_SBUF);
|
|
if (!pixfmt)
|
|
break;
|
|
if (pixfmts && i < len)
|
|
pixfmts[i] = pixfmt;
|
|
}
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
|
|
return i;
|
|
}
|
|
EXPORT_SYMBOL(sde_rotator_inline_get_pixfmt_caps);
|
|
|
|
/*
|
|
* _sde_rotator_inline_cleanup - perform inline related request cleanup
|
|
* This function assumes rot_dev->mgr lock has been taken when called.
|
|
* @handle: Pointer to rotator context
|
|
* @request: Pointer to rotation request
|
|
* return: 0 if success; -EAGAIN if cleanup should be retried
|
|
*/
|
|
static int _sde_rotator_inline_cleanup(void *handle,
|
|
struct sde_rotator_request *request)
|
|
{
|
|
struct sde_rotator_ctx *ctx;
|
|
struct sde_rotator_device *rot_dev;
|
|
int ret;
|
|
|
|
if (!handle || !request) {
|
|
SDEROT_ERR("invalid rotator handle/request\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ctx = handle;
|
|
rot_dev = ctx->rot_dev;
|
|
|
|
if (!rot_dev || !rot_dev->mgr) {
|
|
SDEROT_ERR("invalid rotator device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (request->committed) {
|
|
/* wait until request is finished */
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
mutex_unlock(&rot_dev->lock);
|
|
ret = wait_event_timeout(ctx->wait_queue,
|
|
sde_rotator_is_request_retired(request),
|
|
msecs_to_jiffies(rot_dev->streamoff_timeout));
|
|
mutex_lock(&rot_dev->lock);
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
|
|
if (!ret) {
|
|
SDEROT_ERR("timeout w/o retire s:%d\n",
|
|
ctx->session_id);
|
|
SDEROT_EVTLOG(ctx->session_id, SDE_ROT_EVTLOG_ERROR);
|
|
sde_rotator_abort_inline_request(rot_dev->mgr,
|
|
ctx->private, request->req);
|
|
return -EAGAIN;
|
|
} else if (ret == 1) {
|
|
SDEROT_ERR("timeout w/ retire s:%d\n", ctx->session_id);
|
|
SDEROT_EVTLOG(ctx->session_id, SDE_ROT_EVTLOG_ERROR);
|
|
}
|
|
}
|
|
|
|
sde_rotator_req_finish(rot_dev->mgr, ctx->private, request->req);
|
|
sde_rotator_retire_request(request);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_inline_commit - commit given rotator command
|
|
* @handle: Pointer to rotator context
|
|
* @cmd: Pointer to rotator command
|
|
* @cmd_type: command type - validate/prepare/commit/cleanup
|
|
* return: 0 if success; error code otherwise
|
|
*/
|
|
int sde_rotator_inline_commit(void *handle, struct sde_rotator_inline_cmd *cmd,
|
|
enum sde_rotator_inline_cmd_type cmd_type)
|
|
{
|
|
struct sde_rotator_ctx *ctx;
|
|
struct sde_rotator_device *rot_dev;
|
|
struct sde_rotator_request *request = NULL;
|
|
struct sde_rot_entry_container *req = NULL;
|
|
struct sde_rotation_config rotcfg;
|
|
struct sde_rot_trace_entry rot_trace;
|
|
ktime_t *ts;
|
|
u32 flags = 0;
|
|
int i, ret = 0;
|
|
|
|
if (!handle || !cmd) {
|
|
SDEROT_ERR("invalid rotator handle/cmd\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
ctx = handle;
|
|
rot_dev = ctx->rot_dev;
|
|
|
|
if (!rot_dev || !rot_dev->mgr) {
|
|
SDEROT_ERR("invalid rotator device\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
SDEROT_DBG(
|
|
"s:%d.%u src:(%u,%u,%u,%u)/%ux%u/%c%c%c%c dst:(%u,%u,%u,%u)/%c%c%c%c r:%d f:%d/%d s:%d fps:%u clk:%llu bw:%llu prefill:%llu wb:%d vid:%d cmd:%d\n",
|
|
ctx->session_id, cmd->sequence_id,
|
|
cmd->src_rect_x, cmd->src_rect_y,
|
|
cmd->src_rect_w, cmd->src_rect_h,
|
|
cmd->src_width, cmd->src_height,
|
|
cmd->src_pixfmt >> 0, cmd->src_pixfmt >> 8,
|
|
cmd->src_pixfmt >> 16, cmd->src_pixfmt >> 24,
|
|
cmd->dst_rect_x, cmd->dst_rect_y,
|
|
cmd->dst_rect_w, cmd->dst_rect_h,
|
|
cmd->dst_pixfmt >> 0, cmd->dst_pixfmt >> 8,
|
|
cmd->dst_pixfmt >> 16, cmd->dst_pixfmt >> 24,
|
|
cmd->rot90, cmd->hflip, cmd->vflip, cmd->secure, cmd->fps,
|
|
cmd->clkrate, cmd->data_bw, cmd->prefill_bw,
|
|
cmd->dst_writeback, cmd->video_mode, cmd_type);
|
|
SDEROT_EVTLOG(ctx->session_id, cmd->sequence_id,
|
|
cmd->src_rect_x, cmd->src_rect_y,
|
|
cmd->src_rect_w, cmd->src_rect_h,
|
|
cmd->src_pixfmt,
|
|
cmd->dst_rect_w, cmd->dst_rect_h,
|
|
cmd->dst_pixfmt,
|
|
cmd->fps, cmd->clkrate, cmd->data_bw, cmd->prefill_bw,
|
|
(cmd->rot90 << 0) | (cmd->hflip << 1) | (cmd->vflip << 2) |
|
|
(cmd->secure << 3) | (cmd->dst_writeback << 4) |
|
|
(cmd->video_mode << 5) |
|
|
(cmd_type << 24));
|
|
|
|
mutex_lock(&rot_dev->lock);
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
|
|
if (cmd_type == SDE_ROTATOR_INLINE_CMD_VALIDATE ||
|
|
cmd_type == SDE_ROTATOR_INLINE_CMD_COMMIT) {
|
|
|
|
struct sde_rotation_item item;
|
|
struct sde_rotator_statistics *stats = &rot_dev->stats;
|
|
int scid = llcc_get_slice_id(ctx->slice);
|
|
|
|
/* allocate slot for timestamp */
|
|
ts = stats->ts[stats->count % SDE_ROTATOR_NUM_EVENTS];
|
|
if (cmd_type == SDE_ROTATOR_INLINE_CMD_COMMIT)
|
|
stats->count++;
|
|
|
|
if (cmd->rot90)
|
|
flags |= SDE_ROTATION_90;
|
|
if (cmd->hflip)
|
|
flags |= SDE_ROTATION_FLIP_LR;
|
|
if (cmd->vflip)
|
|
flags |= SDE_ROTATION_FLIP_UD;
|
|
if (cmd->secure)
|
|
flags |= SDE_ROTATION_SECURE;
|
|
|
|
flags |= SDE_ROTATION_EXT_PERF;
|
|
|
|
/* fill in item work structure */
|
|
memset(&item, 0, sizeof(struct sde_rotation_item));
|
|
item.flags = flags | SDE_ROTATION_EXT_IOVA;
|
|
item.trigger = cmd->video_mode ? SDE_ROTATOR_TRIGGER_VIDEO :
|
|
SDE_ROTATOR_TRIGGER_COMMAND;
|
|
item.prefill_bw = cmd->prefill_bw;
|
|
item.session_id = ctx->session_id;
|
|
item.sequence_id = cmd->sequence_id;
|
|
item.src_rect.x = cmd->src_rect_x;
|
|
item.src_rect.y = cmd->src_rect_y;
|
|
item.src_rect.w = cmd->src_rect_w;
|
|
item.src_rect.h = cmd->src_rect_h;
|
|
item.input.width = cmd->src_width;
|
|
item.input.height = cmd->src_height;
|
|
item.input.format = cmd->src_pixfmt;
|
|
|
|
for (i = 0; i < SDE_ROTATOR_INLINE_PLANE_MAX; i++) {
|
|
item.input.planes[i].addr = cmd->src_addr[i];
|
|
item.input.planes[i].len = cmd->src_len[i];
|
|
item.input.planes[i].fd = -1;
|
|
}
|
|
item.input.plane_count = cmd->src_planes;
|
|
item.input.comp_ratio.numer = 1;
|
|
item.input.comp_ratio.denom = 1;
|
|
|
|
item.output.width = cmd->dst_rect_x + cmd->dst_rect_w;
|
|
item.output.height = cmd->dst_rect_y + cmd->dst_rect_h;
|
|
item.dst_rect.x = cmd->dst_rect_x;
|
|
item.dst_rect.y = cmd->dst_rect_y;
|
|
item.dst_rect.w = cmd->dst_rect_w;
|
|
item.dst_rect.h = cmd->dst_rect_h;
|
|
item.output.sbuf = true;
|
|
item.output.scid = scid;
|
|
item.output.writeback = cmd->dst_writeback;
|
|
item.output.format = cmd->dst_pixfmt;
|
|
|
|
for (i = 0; i < SDE_ROTATOR_INLINE_PLANE_MAX; i++) {
|
|
item.output.planes[i].addr = cmd->dst_addr[i];
|
|
item.output.planes[i].len = cmd->dst_len[i];
|
|
item.output.planes[i].fd = -1;
|
|
}
|
|
item.output.plane_count = cmd->dst_planes;
|
|
item.output.comp_ratio.numer = 1;
|
|
item.output.comp_ratio.denom = 1;
|
|
item.sequence_id = ++(ctx->commit_sequence_id);
|
|
item.ts = ts;
|
|
|
|
req = sde_rotator_req_init(rot_dev->mgr, ctx->private,
|
|
&item, 1, 0);
|
|
if (IS_ERR_OR_NULL(req)) {
|
|
SDEROT_ERR("fail allocate request s:%d\n",
|
|
ctx->session_id);
|
|
ret = -ENOMEM;
|
|
goto error_init_request;
|
|
}
|
|
|
|
/* initialize session configuration */
|
|
memset(&rotcfg, 0, sizeof(struct sde_rotation_config));
|
|
rotcfg.flags = flags;
|
|
rotcfg.frame_rate = cmd->fps;
|
|
rotcfg.clk_rate = cmd->clkrate;
|
|
rotcfg.data_bw = cmd->data_bw;
|
|
rotcfg.session_id = ctx->session_id;
|
|
rotcfg.input.width = cmd->src_rect_w;
|
|
rotcfg.input.height = cmd->src_rect_h;
|
|
rotcfg.input.format = cmd->src_pixfmt;
|
|
rotcfg.input.comp_ratio.numer = 1;
|
|
rotcfg.input.comp_ratio.denom = 1;
|
|
rotcfg.output.width = cmd->dst_rect_w;
|
|
rotcfg.output.height = cmd->dst_rect_h;
|
|
rotcfg.output.format = cmd->dst_pixfmt;
|
|
rotcfg.output.comp_ratio.numer = 1;
|
|
rotcfg.output.comp_ratio.denom = 1;
|
|
rotcfg.output.sbuf = true;
|
|
}
|
|
|
|
if (cmd_type == SDE_ROTATOR_INLINE_CMD_VALIDATE) {
|
|
|
|
ret = sde_rotator_session_validate(rot_dev->mgr,
|
|
ctx->private, &rotcfg);
|
|
if (ret) {
|
|
SDEROT_WARN("fail session validation s:%d\n",
|
|
ctx->session_id);
|
|
goto error_session_validate;
|
|
}
|
|
|
|
devm_kfree(rot_dev->dev, req);
|
|
req = NULL;
|
|
|
|
} else if (cmd_type == SDE_ROTATOR_INLINE_CMD_COMMIT) {
|
|
|
|
if (memcmp(&rotcfg, &ctx->rotcfg, sizeof(rotcfg))) {
|
|
ret = sde_rotator_session_config(rot_dev->mgr,
|
|
ctx->private, &rotcfg);
|
|
if (ret) {
|
|
SDEROT_ERR("fail session config s:%d\n",
|
|
ctx->session_id);
|
|
goto error_session_config;
|
|
}
|
|
|
|
ctx->rotcfg = rotcfg;
|
|
}
|
|
|
|
request = list_first_entry_or_null(&ctx->retired_list,
|
|
struct sde_rotator_request, list);
|
|
if (!request) {
|
|
/* should not happen */
|
|
ret = -ENOMEM;
|
|
SDEROT_ERR("no free request s:%d\n", ctx->session_id);
|
|
goto error_retired_list;
|
|
}
|
|
|
|
request->req = req;
|
|
request->sequence_id = req->entries[0].item.sequence_id;
|
|
|
|
spin_lock(&ctx->list_lock);
|
|
list_del_init(&request->list);
|
|
list_add_tail(&request->list, &ctx->pending_list);
|
|
spin_unlock(&ctx->list_lock);
|
|
|
|
ts = req->entries[0].item.ts;
|
|
if (ts) {
|
|
ts[SDE_ROTATOR_TS_SRCQB] = ktime_get();
|
|
ts[SDE_ROTATOR_TS_DSTQB] = ktime_get();
|
|
ts[SDE_ROTATOR_TS_FENCE] = ktime_get();
|
|
} else {
|
|
SDEROT_ERR("invalid stats timestamp\n");
|
|
}
|
|
req->retire_kw = ctx->work_queue.rot_kw;
|
|
req->retire_work = &request->retire_work;
|
|
|
|
/* Set values to pass to trace */
|
|
rot_trace.wb_idx = req->entries[0].item.wb_idx;
|
|
rot_trace.flags = req->entries[0].item.flags;
|
|
rot_trace.input_format = req->entries[0].item.input.format;
|
|
rot_trace.input_width = req->entries[0].item.input.width;
|
|
rot_trace.input_height = req->entries[0].item.input.height;
|
|
rot_trace.src_x = req->entries[0].item.src_rect.x;
|
|
rot_trace.src_y = req->entries[0].item.src_rect.y;
|
|
rot_trace.src_w = req->entries[0].item.src_rect.w;
|
|
rot_trace.src_h = req->entries[0].item.src_rect.h;
|
|
rot_trace.output_format = req->entries[0].item.output.format;
|
|
rot_trace.output_width = req->entries[0].item.output.width;
|
|
rot_trace.output_height = req->entries[0].item.output.height;
|
|
rot_trace.dst_x = req->entries[0].item.dst_rect.x;
|
|
rot_trace.dst_y = req->entries[0].item.dst_rect.y;
|
|
rot_trace.dst_w = req->entries[0].item.dst_rect.w;
|
|
rot_trace.dst_h = req->entries[0].item.dst_rect.h;
|
|
|
|
|
|
trace_rot_entry_fence(
|
|
ctx->session_id, cmd->sequence_id, &rot_trace);
|
|
|
|
ret = sde_rotator_handle_request_common(
|
|
rot_dev->mgr, ctx->private, req);
|
|
if (ret) {
|
|
SDEROT_ERR("fail handle request s:%d\n",
|
|
ctx->session_id);
|
|
goto error_handle_request;
|
|
}
|
|
|
|
sde_rotator_req_reset_start(rot_dev->mgr, req);
|
|
|
|
sde_rotator_queue_request(rot_dev->mgr, ctx->private, req);
|
|
|
|
request->committed = true;
|
|
|
|
/* save request in private handle */
|
|
cmd->priv_handle = request;
|
|
|
|
} else if (cmd_type == SDE_ROTATOR_INLINE_CMD_START) {
|
|
if (!cmd->priv_handle) {
|
|
ret = -EINVAL;
|
|
SDEROT_ERR("invalid private handle\n");
|
|
goto error_invalid_handle;
|
|
}
|
|
|
|
request = cmd->priv_handle;
|
|
sde_rotator_req_set_start(rot_dev->mgr, request->req);
|
|
} else if (cmd_type == SDE_ROTATOR_INLINE_CMD_CLEANUP) {
|
|
if (!cmd->priv_handle) {
|
|
ret = -EINVAL;
|
|
SDEROT_ERR("invalid private handle\n");
|
|
goto error_invalid_handle;
|
|
}
|
|
|
|
request = cmd->priv_handle;
|
|
|
|
/* attempt single retry if first cleanup attempt failed */
|
|
if (_sde_rotator_inline_cleanup(handle, request) == -EAGAIN)
|
|
_sde_rotator_inline_cleanup(handle, request);
|
|
|
|
cmd->priv_handle = NULL;
|
|
} else if (cmd_type == SDE_ROTATOR_INLINE_CMD_ABORT) {
|
|
if (!cmd->priv_handle) {
|
|
ret = -EINVAL;
|
|
SDEROT_ERR("invalid private handle\n");
|
|
goto error_invalid_handle;
|
|
}
|
|
|
|
request = cmd->priv_handle;
|
|
if (!sde_rotator_is_request_retired(request))
|
|
sde_rotator_abort_inline_request(rot_dev->mgr,
|
|
ctx->private, request->req);
|
|
}
|
|
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
mutex_unlock(&rot_dev->lock);
|
|
return 0;
|
|
|
|
error_handle_request:
|
|
sde_rotator_update_retire_sequence(request);
|
|
sde_rotator_retire_request(request);
|
|
error_retired_list:
|
|
error_session_validate:
|
|
error_session_config:
|
|
devm_kfree(rot_dev->dev, req);
|
|
error_invalid_handle:
|
|
error_init_request:
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
mutex_unlock(&rot_dev->lock);
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(sde_rotator_inline_commit);
|
|
|
|
void sde_rotator_inline_reg_dump(struct platform_device *pdev)
|
|
{
|
|
struct sde_rotator_device *rot_dev;
|
|
|
|
if (!pdev) {
|
|
SDEROT_ERR("invalid platform device\n");
|
|
return;
|
|
}
|
|
|
|
rot_dev = (struct sde_rotator_device *) platform_get_drvdata(pdev);
|
|
if (!rot_dev || !rot_dev->mgr) {
|
|
SDEROT_ERR("invalid rotator device\n");
|
|
return;
|
|
}
|
|
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
sde_rotator_core_dump(rot_dev->mgr);
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
}
|
|
EXPORT_SYMBOL(sde_rotator_inline_reg_dump);
|
|
|
|
/*
|
|
* sde_rotator_open - Rotator device open method.
|
|
* @file: Pointer to file struct.
|
|
*/
|
|
static int sde_rotator_open(struct file *file)
|
|
{
|
|
struct sde_rotator_device *rot_dev = video_drvdata(file);
|
|
struct sde_rotator_ctx *ctx;
|
|
int ret = 0;
|
|
|
|
ctx = sde_rotator_ctx_open(rot_dev, file);
|
|
if (IS_ERR_OR_NULL(ctx)) {
|
|
SDEDEV_DBG(rot_dev->dev, "failed to open %d\n", ret);
|
|
ret = PTR_ERR(ctx);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_release - Rotator device release method.
|
|
* @file: Pointer to file struct.
|
|
*/
|
|
static int sde_rotator_release(struct file *file)
|
|
{
|
|
struct sde_rotator_ctx *ctx =
|
|
sde_rotator_ctx_from_fh(file->private_data);
|
|
|
|
return sde_rotator_ctx_release(ctx, file);
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_poll - rotator device pool method.
|
|
* @file: Pointer to file struct.
|
|
* @wait: Pointer to poll table struct.
|
|
*/
|
|
static unsigned int sde_rotator_poll(struct file *file,
|
|
struct poll_table_struct *wait)
|
|
{
|
|
struct sde_rotator_device *rot_dev = video_drvdata(file);
|
|
struct sde_rotator_ctx *ctx =
|
|
sde_rotator_ctx_from_fh(file->private_data);
|
|
int ret;
|
|
|
|
mutex_lock(&rot_dev->lock);
|
|
ret = v4l2_m2m_poll(file, ctx->fh.m2m_ctx, wait);
|
|
mutex_unlock(&rot_dev->lock);
|
|
return ret;
|
|
}
|
|
|
|
/* rotator device file operations callbacks */
|
|
static const struct v4l2_file_operations sde_rotator_fops = {
|
|
.owner = THIS_MODULE,
|
|
.open = sde_rotator_open,
|
|
.release = sde_rotator_release,
|
|
.poll = sde_rotator_poll,
|
|
.unlocked_ioctl = video_ioctl2,
|
|
#if IS_ENABLED(CONFIG_COMPAT)
|
|
.compat_ioctl32 = sde_rotator_compat_ioctl32,
|
|
#endif /* CONFIG_COMPAT */
|
|
};
|
|
|
|
/*
|
|
* sde_rotator_querycap - V4l2 ioctl query capability handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @cap: Pointer to v4l2_capability struct need to be filled.
|
|
*/
|
|
static int sde_rotator_querycap(struct file *file,
|
|
void *fh, struct v4l2_capability *cap)
|
|
{
|
|
cap->bus_info[0] = 0;
|
|
strlcpy(cap->driver, SDE_ROTATOR_DRV_NAME, sizeof(cap->driver));
|
|
strlcpy(cap->card, SDE_ROTATOR_DRV_NAME, sizeof(cap->card));
|
|
cap->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M |
|
|
V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_CAPTURE;
|
|
cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_enum_fmt_vid_cap - V4l2 ioctl enumerate output format handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @f: Pointer to v4l2_fmtdesc struct need to be filled.
|
|
*/
|
|
static int sde_rotator_enum_fmt_vid_cap(struct file *file,
|
|
void *fh, struct v4l2_fmtdesc *f)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
struct sde_mdp_format_params *fmt;
|
|
u32 i, index, pixfmt;
|
|
bool found = false;
|
|
|
|
for (i = 0, index = 0; index <= f->index; i++) {
|
|
pixfmt = sde_rotator_get_pixfmt(rot_dev->mgr, i, false,
|
|
SDE_ROTATOR_MODE_OFFLINE);
|
|
if (!pixfmt)
|
|
return -EINVAL;
|
|
|
|
fmt = sde_get_format_params(pixfmt);
|
|
if (!fmt)
|
|
return -EINVAL;
|
|
|
|
if (sde_mdp_is_private_format(fmt))
|
|
continue;
|
|
|
|
if (index == f->index) {
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
if (!found)
|
|
return -EINVAL;
|
|
|
|
f->pixelformat = pixfmt;
|
|
strlcpy(f->description, fmt->description, sizeof(f->description));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_enum_fmt_vid_out - V4l2 ioctl enumerate capture format handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @f: Pointer to v4l2_fmtdesc struct need to be filled.
|
|
*/
|
|
static int sde_rotator_enum_fmt_vid_out(struct file *file,
|
|
void *fh, struct v4l2_fmtdesc *f)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
struct sde_mdp_format_params *fmt;
|
|
u32 i, index, pixfmt;
|
|
bool found = false;
|
|
|
|
for (i = 0, index = 0; index <= f->index; i++) {
|
|
pixfmt = sde_rotator_get_pixfmt(rot_dev->mgr, i, true,
|
|
SDE_ROTATOR_MODE_OFFLINE);
|
|
if (!pixfmt)
|
|
return -EINVAL;
|
|
|
|
fmt = sde_get_format_params(pixfmt);
|
|
if (!fmt)
|
|
return -EINVAL;
|
|
|
|
if (sde_mdp_is_private_format(fmt))
|
|
continue;
|
|
|
|
if (index == f->index) {
|
|
found = true;
|
|
break;
|
|
}
|
|
|
|
index++;
|
|
}
|
|
|
|
if (!found)
|
|
return -EINVAL;
|
|
|
|
f->pixelformat = pixfmt;
|
|
strlcpy(f->description, fmt->description, sizeof(f->description));
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_g_fmt_cap - V4l2 ioctl get capture format handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @f: Pointer to v4l2_format struct need to be filled.
|
|
*/
|
|
static int sde_rotator_g_fmt_cap(struct file *file, void *fh,
|
|
struct v4l2_format *f)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
|
|
*f = ctx->format_cap;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_g_fmt_out - V4l2 ioctl get output format handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @f: Pointer to v4l2_format struct need to be filled.
|
|
*/
|
|
static int sde_rotator_g_fmt_out(struct file *file, void *fh,
|
|
struct v4l2_format *f)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
|
|
*f = ctx->format_out;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_try_fmt_vid_cap - V4l2 ioctl try capture format handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @f: Pointer to v4l2_format struct.
|
|
*/
|
|
static int sde_rotator_try_fmt_vid_cap(struct file *file,
|
|
void *fh, struct v4l2_format *f)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
struct sde_rotation_config config;
|
|
int ret;
|
|
|
|
if ((f->fmt.pix.width == 0) || (f->fmt.pix.height == 0)) {
|
|
SDEDEV_WARN(ctx->rot_dev->dev,
|
|
"Not supporting 0 width/height: %dx%d\n",
|
|
f->fmt.pix.width, f->fmt.pix.height);
|
|
return -EINVAL;
|
|
}
|
|
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
sde_rotator_get_config_from_ctx(ctx, &config);
|
|
config.output.format = f->fmt.pix.pixelformat;
|
|
config.output.width = f->fmt.pix.width;
|
|
config.output.height = f->fmt.pix.height;
|
|
config.flags |= SDE_ROTATION_VERIFY_INPUT_ONLY;
|
|
ret = sde_rotator_verify_config_output(rot_dev->mgr, &config);
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
if (ret) {
|
|
if ((config.output.width == f->fmt.pix.width) &&
|
|
(config.output.height == f->fmt.pix.height)) {
|
|
SDEDEV_WARN(ctx->rot_dev->dev,
|
|
"invalid capture format 0x%8.8x %dx%d\n",
|
|
f->fmt.pix.pixelformat,
|
|
f->fmt.pix.width,
|
|
f->fmt.pix.height);
|
|
return -EINVAL;
|
|
}
|
|
f->fmt.pix.width = config.output.width;
|
|
f->fmt.pix.height = config.output.height;
|
|
}
|
|
|
|
sde_rotator_format_recalc(f);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_try_fmt_vid_out - V4l2 ioctl try output format handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @f: Pointer to v4l2_format struct.
|
|
*/
|
|
static int sde_rotator_try_fmt_vid_out(struct file *file,
|
|
void *fh, struct v4l2_format *f)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
struct sde_rotation_config config;
|
|
int ret;
|
|
|
|
if ((f->fmt.pix.width == 0) || (f->fmt.pix.height == 0)) {
|
|
SDEDEV_WARN(ctx->rot_dev->dev,
|
|
"Not supporting 0 width/height: %dx%d\n",
|
|
f->fmt.pix.width, f->fmt.pix.height);
|
|
return -EINVAL;
|
|
}
|
|
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
sde_rotator_get_config_from_ctx(ctx, &config);
|
|
config.input.format = f->fmt.pix.pixelformat;
|
|
config.input.width = f->fmt.pix.width;
|
|
config.input.height = f->fmt.pix.height;
|
|
config.flags |= SDE_ROTATION_VERIFY_INPUT_ONLY;
|
|
ret = sde_rotator_verify_config_input(rot_dev->mgr, &config);
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
if (ret) {
|
|
if ((config.input.width == f->fmt.pix.width) &&
|
|
(config.input.height == f->fmt.pix.height)) {
|
|
SDEDEV_WARN(ctx->rot_dev->dev,
|
|
"invalid output format 0x%8.8x %dx%d\n",
|
|
f->fmt.pix.pixelformat,
|
|
f->fmt.pix.width,
|
|
f->fmt.pix.height);
|
|
return -EINVAL;
|
|
}
|
|
f->fmt.pix.width = config.input.width;
|
|
f->fmt.pix.height = config.input.height;
|
|
}
|
|
|
|
sde_rotator_format_recalc(f);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_s_fmt_vid_cap - V4l2 ioctl set capture format handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @f: Pointer to v4l2_format struct.
|
|
*/
|
|
static int sde_rotator_s_fmt_vid_cap(struct file *file,
|
|
void *fh, struct v4l2_format *f)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
int ret;
|
|
|
|
ret = sde_rotator_try_fmt_vid_cap(file, fh, f);
|
|
if (ret)
|
|
return -EINVAL;
|
|
|
|
/* Initialize crop */
|
|
ctx->crop_cap.top = 0;
|
|
ctx->crop_cap.left = 0;
|
|
ctx->crop_cap.width = f->fmt.pix.width;
|
|
ctx->crop_cap.height = f->fmt.pix.height;
|
|
|
|
ctx->format_cap = *f;
|
|
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"s_fmt s:%d t:%d fmt:0x%8.8x field:%u (%u,%u)\n",
|
|
ctx->session_id, f->type,
|
|
f->fmt.pix.pixelformat,
|
|
f->fmt.pix.field,
|
|
f->fmt.pix.width, f->fmt.pix.height);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_s_fmt_vid_out - V4l2 ioctl set output format handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @f: Pointer to v4l2_format struct.
|
|
*/
|
|
static int sde_rotator_s_fmt_vid_out(struct file *file,
|
|
void *fh, struct v4l2_format *f)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
int ret;
|
|
|
|
ret = sde_rotator_try_fmt_vid_out(file, fh, f);
|
|
if (ret)
|
|
return -EINVAL;
|
|
|
|
/* Initialize crop */
|
|
ctx->crop_out.top = 0;
|
|
ctx->crop_out.left = 0;
|
|
ctx->crop_out.width = f->fmt.pix.width;
|
|
ctx->crop_out.height = f->fmt.pix.height;
|
|
|
|
ctx->format_out = *f;
|
|
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"s_fmt s:%d t:%d fmt:0x%8.8x field:%u (%u,%u)\n",
|
|
ctx->session_id, f->type,
|
|
f->fmt.pix.pixelformat,
|
|
f->fmt.pix.field,
|
|
f->fmt.pix.width, f->fmt.pix.height);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_reqbufs - V4l2 ioctl request buffers handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @req: Pointer to v4l2_requestbuffer struct.
|
|
*/
|
|
static int sde_rotator_reqbufs(struct file *file,
|
|
void *fh, struct v4l2_requestbuffers *req)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
|
|
return v4l2_m2m_reqbufs(file, ctx->fh.m2m_ctx, req);
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_qbuf - V4l2 ioctl queue buffer handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @buf: Pointer to v4l2_buffer struct.
|
|
*/
|
|
static int sde_rotator_qbuf(struct file *file, void *fh,
|
|
struct v4l2_buffer *buf)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
int ret;
|
|
|
|
/* create fence for capture buffer */
|
|
if ((buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
&& (buf->index < ctx->nbuf_cap)) {
|
|
int idx = buf->index;
|
|
|
|
ctx->vbinfo_cap[idx].fd = -1;
|
|
ctx->vbinfo_cap[idx].fence = sde_rotator_get_sync_fence(
|
|
ctx->work_queue.timeline, NULL,
|
|
&ctx->vbinfo_cap[idx].fence_ts);
|
|
ctx->vbinfo_cap[idx].qbuf_ts = ktime_get();
|
|
ctx->vbinfo_cap[idx].dqbuf_ts = NULL;
|
|
SDEDEV_DBG(ctx->rot_dev->dev,
|
|
"create buffer fence s:%d.%u i:%d f:%pK\n",
|
|
ctx->session_id,
|
|
ctx->vbinfo_cap[idx].fence_ts,
|
|
idx,
|
|
ctx->vbinfo_cap[idx].fence);
|
|
} else if ((buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
|
|
&& (buf->index < ctx->nbuf_out)) {
|
|
int idx = buf->index;
|
|
|
|
ctx->vbinfo_out[idx].qbuf_ts = ktime_get();
|
|
ctx->vbinfo_out[idx].dqbuf_ts = NULL;
|
|
}
|
|
|
|
ret = v4l2_m2m_qbuf(file, ctx->fh.m2m_ctx, buf);
|
|
if (ret < 0)
|
|
SDEDEV_ERR(ctx->rot_dev->dev, "fail qbuf s:%d t:%d r:%d\n",
|
|
ctx->session_id, buf->type, ret);
|
|
SDEROT_EVTLOG(buf->type, buf->bytesused, buf->length, buf->m.fd, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_dqbuf - V4l2 ioctl dequeue buffer handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @buf: Pointer to v4l2_buffer struct.
|
|
*/
|
|
static int sde_rotator_dqbuf(struct file *file,
|
|
void *fh, struct v4l2_buffer *buf)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
int ret;
|
|
|
|
ret = v4l2_m2m_dqbuf(file, ctx->fh.m2m_ctx, buf);
|
|
|
|
if (ret) {
|
|
SDEDEV_ERR(ctx->rot_dev->dev,
|
|
"fail dqbuf s:%d t:%d i:%d r:%d\n",
|
|
ctx->session_id, buf->type, buf->index, ret);
|
|
return ret;
|
|
}
|
|
|
|
/* clear fence for buffer */
|
|
if ((buf->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
&& (buf->index < ctx->nbuf_cap)) {
|
|
int idx = buf->index;
|
|
|
|
if (ctx->vbinfo_cap[idx].fence) {
|
|
/* fence is not used */
|
|
SDEDEV_DBG(ctx->rot_dev->dev, "put fence s:%d i:%d\n",
|
|
ctx->session_id, idx);
|
|
sde_rotator_put_sync_fence(ctx->vbinfo_cap[idx].fence);
|
|
}
|
|
ctx->vbinfo_cap[idx].fence = NULL;
|
|
ctx->vbinfo_cap[idx].fd = -1;
|
|
if (ctx->vbinfo_cap[idx].dqbuf_ts)
|
|
*(ctx->vbinfo_cap[idx].dqbuf_ts) = ktime_get();
|
|
} else if ((buf->type == V4L2_BUF_TYPE_VIDEO_OUTPUT)
|
|
&& (buf->index < ctx->nbuf_out)) {
|
|
int idx = buf->index;
|
|
|
|
ctx->vbinfo_out[idx].fence = NULL;
|
|
ctx->vbinfo_out[idx].fd = -1;
|
|
if (ctx->vbinfo_out[idx].dqbuf_ts)
|
|
*(ctx->vbinfo_out[idx].dqbuf_ts) = ktime_get();
|
|
} else {
|
|
SDEDEV_WARN(ctx->rot_dev->dev, "invalid dq s:%d t:%d i:%d\n",
|
|
ctx->session_id, buf->type, buf->index);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_querybuf - V4l2 ioctl query buffer handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @buf: Pointer to v4l2_buffer struct.
|
|
*/
|
|
static int sde_rotator_querybuf(struct file *file,
|
|
void *fh, struct v4l2_buffer *buf)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
|
|
return v4l2_m2m_querybuf(file, ctx->fh.m2m_ctx, buf);
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_streamon - V4l2 ioctl stream on handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @buf_type: V4l2 buffer type.
|
|
*/
|
|
static int sde_rotator_streamon(struct file *file,
|
|
void *fh, enum v4l2_buf_type buf_type)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
struct sde_rotation_config config;
|
|
struct vb2_queue *vq;
|
|
int ret;
|
|
|
|
SDEDEV_DBG(ctx->rot_dev->dev, "stream on s:%d t:%d\n",
|
|
ctx->session_id, buf_type);
|
|
|
|
vq = v4l2_m2m_get_vq(ctx->fh.m2m_ctx,
|
|
buf_type == V4L2_BUF_TYPE_VIDEO_OUTPUT ?
|
|
V4L2_BUF_TYPE_VIDEO_CAPTURE :
|
|
V4L2_BUF_TYPE_VIDEO_OUTPUT);
|
|
|
|
if (!vq) {
|
|
SDEDEV_ERR(ctx->rot_dev->dev, "fail to get vq on s:%d t:%d\n",
|
|
ctx->session_id, buf_type);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (vb2_is_streaming(vq)) {
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
sde_rotator_get_config_from_ctx(ctx, &config);
|
|
config.flags &= ~SDE_ROTATION_VERIFY_INPUT_ONLY;
|
|
ret = sde_rotator_session_config(rot_dev->mgr, ctx->private,
|
|
&config);
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
if (ret < 0) {
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"fail config in stream on s:%d t:%d r:%d\n",
|
|
ctx->session_id, buf_type, ret);
|
|
return ret;
|
|
}
|
|
ctx->rotcfg = config;
|
|
}
|
|
|
|
ret = v4l2_m2m_streamon(file, ctx->fh.m2m_ctx, buf_type);
|
|
if (ret < 0)
|
|
SDEDEV_ERR(ctx->rot_dev->dev, "fail stream on s:%d t:%d\n",
|
|
ctx->session_id, buf_type);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_streamoff - V4l2 ioctl stream off handler.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @buf_type: V4l2 buffer type.
|
|
*/
|
|
static int sde_rotator_streamoff(struct file *file,
|
|
void *fh, enum v4l2_buf_type buf_type)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
int ret;
|
|
|
|
SDEDEV_DBG(ctx->rot_dev->dev, "stream off s:%d t:%d\n",
|
|
ctx->session_id, buf_type);
|
|
|
|
ret = v4l2_m2m_streamoff(file, ctx->fh.m2m_ctx, buf_type);
|
|
if (ret < 0)
|
|
SDEDEV_ERR(ctx->rot_dev->dev, "fail stream off s:%d t:%d\n",
|
|
ctx->session_id, buf_type);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_g_selection - V4l2 ioctl get crop.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @selection: Pointer to v4l2_selection struct need to be set.
|
|
*/
|
|
static int sde_rotator_g_selection(struct file *file, void *fh,
|
|
struct v4l2_selection *selection)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
|
|
switch (selection->type) {
|
|
case V4L2_BUF_TYPE_VIDEO_OUTPUT:
|
|
selection->r = ctx->crop_out;
|
|
break;
|
|
case V4L2_BUF_TYPE_VIDEO_CAPTURE:
|
|
selection->r = ctx->crop_cap;
|
|
break;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_s_selection - V4l2 ioctl set crop.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @selection: Pointer to v4l2_selection struct need to be set.
|
|
*/
|
|
static int sde_rotator_s_selection(struct file *file, void *fh,
|
|
struct v4l2_selection *selection)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
struct sde_rotation_item item;
|
|
struct v4l2_rect rect;
|
|
|
|
sde_rotator_get_item_from_ctx(ctx, &item);
|
|
|
|
rect.left = max_t(__u32, selection->r.left, 0);
|
|
rect.top = max_t(__u32, selection->r.top, 0);
|
|
rect.height = max_t(__u32, selection->r.height, 0);
|
|
rect.width = max_t(__u32, selection->r.width, 0);
|
|
|
|
if (selection->type == V4L2_BUF_TYPE_VIDEO_OUTPUT) {
|
|
rect.left = min_t(__u32, rect.left,
|
|
ctx->format_out.fmt.pix.width - 1);
|
|
rect.top = min_t(__u32, rect.top,
|
|
ctx->format_out.fmt.pix.height - 1);
|
|
rect.width = min_t(__u32, rect.width,
|
|
(ctx->format_out.fmt.pix.width - rect.left));
|
|
rect.height = min_t(__u32, rect.height,
|
|
(ctx->format_out.fmt.pix.height - rect.top));
|
|
|
|
item.src_rect.x = rect.left;
|
|
item.src_rect.y = rect.top;
|
|
item.src_rect.w = rect.width;
|
|
item.src_rect.h = rect.height;
|
|
|
|
sde_rotator_validate_item(ctx, &item);
|
|
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"s_crop s:%d t:%d (%u,%u,%u,%u)->(%u,%u,%u,%u)\n",
|
|
ctx->session_id, selection->type,
|
|
selection->r.left, selection->r.top,
|
|
selection->r.width, selection->r.height,
|
|
item.src_rect.x, item.src_rect.y,
|
|
item.src_rect.w, item.src_rect.h);
|
|
|
|
ctx->crop_out.left = item.src_rect.x;
|
|
ctx->crop_out.top = item.src_rect.y;
|
|
ctx->crop_out.width = item.src_rect.w;
|
|
ctx->crop_out.height = item.src_rect.h;
|
|
} else if (selection->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) {
|
|
rect.left = min_t(__u32, rect.left,
|
|
ctx->format_cap.fmt.pix.width - 1);
|
|
rect.top = min_t(__u32, rect.top,
|
|
ctx->format_cap.fmt.pix.height - 1);
|
|
rect.width = min_t(__u32, rect.width,
|
|
(ctx->format_cap.fmt.pix.width - rect.left));
|
|
rect.height = min_t(__u32, rect.height,
|
|
(ctx->format_cap.fmt.pix.height - rect.top));
|
|
|
|
item.dst_rect.x = rect.left;
|
|
item.dst_rect.y = rect.top;
|
|
item.dst_rect.w = rect.width;
|
|
item.dst_rect.h = rect.height;
|
|
|
|
sde_rotator_validate_item(ctx, &item);
|
|
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"s_crop s:%d t:%d (%u,%u,%u,%u)->(%u,%u,%u,%u)\n",
|
|
ctx->session_id, selection->type,
|
|
selection->r.left, selection->r.top,
|
|
selection->r.width, selection->r.height,
|
|
item.dst_rect.x, item.dst_rect.y,
|
|
item.dst_rect.w, item.dst_rect.h);
|
|
|
|
ctx->crop_cap.left = item.dst_rect.x;
|
|
ctx->crop_cap.top = item.dst_rect.y;
|
|
ctx->crop_cap.width = item.dst_rect.w;
|
|
ctx->crop_cap.height = item.dst_rect.h;
|
|
} else {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_g_parm - V4l2 ioctl get parm.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @a: Pointer to v4l2_streamparm struct need to be filled.
|
|
*/
|
|
static int sde_rotator_g_parm(struct file *file, void *fh,
|
|
struct v4l2_streamparm *a)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
|
|
/* Get param is supported only for input buffers */
|
|
if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
|
|
return -EINVAL;
|
|
|
|
a->parm.output.capability = 0;
|
|
a->parm.output.extendedmode = 0;
|
|
a->parm.output.outputmode = 0;
|
|
a->parm.output.writebuffers = 0;
|
|
a->parm.output.timeperframe = ctx->timeperframe;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_s_parm - V4l2 ioctl set parm.
|
|
* @file: Pointer to file struct.
|
|
* @fh: V4l2 File handle.
|
|
* @a: Pointer to v4l2_streamparm struct need to be set.
|
|
*/
|
|
static int sde_rotator_s_parm(struct file *file, void *fh,
|
|
struct v4l2_streamparm *a)
|
|
{
|
|
struct sde_rotator_ctx *ctx = sde_rotator_ctx_from_fh(fh);
|
|
|
|
/* Set param is supported only for input buffers */
|
|
if (a->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
|
|
return -EINVAL;
|
|
|
|
if (!a->parm.output.timeperframe.numerator ||
|
|
!a->parm.output.timeperframe.denominator)
|
|
return -EINVAL;
|
|
|
|
ctx->timeperframe = a->parm.output.timeperframe;
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_private_ioctl - V4l2 private ioctl handler.
|
|
* @file: Pointer to file struct.
|
|
* @fd: V4l2 device file handle.
|
|
* @valid_prio: Priority ioctl valid flag.
|
|
* @cmd: Ioctl command.
|
|
* @arg: Ioctl argument.
|
|
*/
|
|
static long sde_rotator_private_ioctl(struct file *file, void *fh,
|
|
bool valid_prio, unsigned int cmd, void *arg)
|
|
{
|
|
struct sde_rotator_ctx *ctx =
|
|
sde_rotator_ctx_from_fh(file->private_data);
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
struct msm_sde_rotator_fence *fence = arg;
|
|
struct msm_sde_rotator_comp_ratio *comp_ratio = arg;
|
|
struct sde_rotator_vbinfo *vbinfo;
|
|
int ret;
|
|
|
|
switch (cmd) {
|
|
case VIDIOC_S_SDE_ROTATOR_FENCE:
|
|
if (!fence)
|
|
return -EINVAL;
|
|
|
|
if (fence->type != V4L2_BUF_TYPE_VIDEO_OUTPUT)
|
|
return -EINVAL;
|
|
|
|
if (fence->index >= ctx->nbuf_out)
|
|
return -EINVAL;
|
|
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"VIDIOC_S_SDE_ROTATOR_FENCE s:%d i:%d fd:%d\n",
|
|
ctx->session_id, fence->index,
|
|
fence->fd);
|
|
|
|
vbinfo = &ctx->vbinfo_out[fence->index];
|
|
|
|
if (vbinfo->fd >= 0) {
|
|
if (vbinfo->fence) {
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"put fence s:%d t:%d i:%d\n",
|
|
ctx->session_id,
|
|
fence->type, fence->index);
|
|
sde_rotator_put_sync_fence(vbinfo->fence);
|
|
}
|
|
vbinfo->fence = NULL;
|
|
vbinfo->fd = -1;
|
|
}
|
|
|
|
vbinfo->fd = fence->fd;
|
|
if (vbinfo->fd >= 0) {
|
|
vbinfo->fence =
|
|
sde_rotator_get_fd_sync_fence(vbinfo->fd);
|
|
if (!vbinfo->fence) {
|
|
SDEDEV_WARN(rot_dev->dev,
|
|
"invalid input fence fd s:%d fd:%d\n",
|
|
ctx->session_id, vbinfo->fd);
|
|
vbinfo->fd = -1;
|
|
return -EINVAL;
|
|
}
|
|
} else {
|
|
vbinfo->fence = NULL;
|
|
}
|
|
break;
|
|
case VIDIOC_G_SDE_ROTATOR_FENCE:
|
|
if (!fence)
|
|
return -EINVAL;
|
|
|
|
if (fence->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
|
|
return -EINVAL;
|
|
|
|
if (fence->index >= ctx->nbuf_cap)
|
|
return -EINVAL;
|
|
|
|
vbinfo = &ctx->vbinfo_cap[fence->index];
|
|
|
|
if (!vbinfo)
|
|
return -EINVAL;
|
|
|
|
if (vbinfo->fence) {
|
|
ret = sde_rotator_get_sync_fence_fd(vbinfo->fence);
|
|
if (ret < 0) {
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"fail get fence fd s:%d\n",
|
|
ctx->session_id);
|
|
return ret;
|
|
}
|
|
|
|
/**
|
|
* Cache fence descriptor in case user calls this
|
|
* ioctl multiple times. Cached value would be stale
|
|
* if user duplicated and closed old descriptor.
|
|
*/
|
|
vbinfo->fd = ret;
|
|
} else if (!sde_rotator_get_fd_sync_fence(vbinfo->fd)) {
|
|
/**
|
|
* User has closed cached fence descriptor.
|
|
* Invalidate descriptor cache.
|
|
*/
|
|
vbinfo->fd = -1;
|
|
}
|
|
fence->fd = vbinfo->fd;
|
|
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"VIDIOC_G_SDE_ROTATOR_FENCE s:%d i:%d fd:%d\n",
|
|
ctx->session_id, fence->index,
|
|
fence->fd);
|
|
break;
|
|
case VIDIOC_S_SDE_ROTATOR_COMP_RATIO:
|
|
if (!comp_ratio)
|
|
return -EINVAL;
|
|
else if (!comp_ratio->numer || !comp_ratio->denom)
|
|
return -EINVAL;
|
|
else if (comp_ratio->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
|
|
comp_ratio->index < ctx->nbuf_out)
|
|
vbinfo = &ctx->vbinfo_out[comp_ratio->index];
|
|
else if (comp_ratio->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
|
|
comp_ratio->index < ctx->nbuf_cap)
|
|
vbinfo = &ctx->vbinfo_cap[comp_ratio->index];
|
|
else
|
|
return -EINVAL;
|
|
|
|
vbinfo->comp_ratio.numer = comp_ratio->numer;
|
|
vbinfo->comp_ratio.denom = comp_ratio->denom;
|
|
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"VIDIOC_S_SDE_ROTATOR_COMP_RATIO s:%d i:%d t:%d cr:%u/%u\n",
|
|
ctx->session_id, comp_ratio->index,
|
|
comp_ratio->type,
|
|
vbinfo->comp_ratio.numer,
|
|
vbinfo->comp_ratio.denom);
|
|
break;
|
|
case VIDIOC_G_SDE_ROTATOR_COMP_RATIO:
|
|
if (!comp_ratio)
|
|
return -EINVAL;
|
|
else if (comp_ratio->type == V4L2_BUF_TYPE_VIDEO_OUTPUT &&
|
|
comp_ratio->index < ctx->nbuf_out)
|
|
vbinfo = &ctx->vbinfo_out[comp_ratio->index];
|
|
else if (comp_ratio->type == V4L2_BUF_TYPE_VIDEO_CAPTURE &&
|
|
comp_ratio->index < ctx->nbuf_cap)
|
|
vbinfo = &ctx->vbinfo_cap[comp_ratio->index];
|
|
else
|
|
return -EINVAL;
|
|
|
|
comp_ratio->numer = vbinfo->comp_ratio.numer;
|
|
comp_ratio->denom = vbinfo->comp_ratio.denom;
|
|
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"VIDIOC_G_SDE_ROTATOR_COMP_RATIO s:%d i:%d t:%d cr:%u/%u\n",
|
|
ctx->session_id, comp_ratio->index,
|
|
comp_ratio->type,
|
|
comp_ratio->numer,
|
|
comp_ratio->denom);
|
|
break;
|
|
default:
|
|
SDEDEV_WARN(rot_dev->dev, "invalid ioctl type %x\n", cmd);
|
|
return -ENOTTY;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if IS_ENABLED(CONFIG_COMPAT)
|
|
/*
|
|
* sde_rotator_compat_ioctl32 - Compat ioctl handler function.
|
|
* @file: Pointer to file struct.
|
|
* @cmd: Ioctl command.
|
|
* @arg: Ioctl argument.
|
|
*/
|
|
static long sde_rotator_compat_ioctl32(struct file *file,
|
|
unsigned int cmd, unsigned long arg)
|
|
{
|
|
struct video_device *vdev = video_devdata(file);
|
|
struct sde_rotator_ctx *ctx =
|
|
sde_rotator_ctx_from_fh(file->private_data);
|
|
long ret;
|
|
|
|
mutex_lock(vdev->lock);
|
|
|
|
switch (cmd) {
|
|
case VIDIOC_S_SDE_ROTATOR_FENCE:
|
|
case VIDIOC_G_SDE_ROTATOR_FENCE:
|
|
{
|
|
struct msm_sde_rotator_fence fence;
|
|
|
|
if (copy_from_user(&fence, (void __user *)arg,
|
|
sizeof(struct msm_sde_rotator_fence)))
|
|
goto ioctl32_error;
|
|
|
|
ret = sde_rotator_private_ioctl(file, file->private_data,
|
|
0, cmd, (void *)&fence);
|
|
|
|
if (copy_to_user((void __user *)arg, &fence,
|
|
sizeof(struct msm_sde_rotator_fence)))
|
|
goto ioctl32_error;
|
|
|
|
break;
|
|
}
|
|
case VIDIOC_S_SDE_ROTATOR_COMP_RATIO:
|
|
case VIDIOC_G_SDE_ROTATOR_COMP_RATIO:
|
|
{
|
|
struct msm_sde_rotator_comp_ratio comp_ratio;
|
|
|
|
if (copy_from_user(&comp_ratio, (void __user *)arg,
|
|
sizeof(struct msm_sde_rotator_comp_ratio)))
|
|
goto ioctl32_error;
|
|
|
|
ret = sde_rotator_private_ioctl(file, file->private_data,
|
|
0, cmd, (void *)&comp_ratio);
|
|
|
|
if (copy_to_user((void __user *)arg, &comp_ratio,
|
|
sizeof(struct msm_sde_rotator_comp_ratio)))
|
|
goto ioctl32_error;
|
|
|
|
break;
|
|
}
|
|
default:
|
|
SDEDEV_ERR(ctx->rot_dev->dev, "invalid ioctl32 type:%x\n", cmd);
|
|
ret = -ENOIOCTLCMD;
|
|
break;
|
|
|
|
}
|
|
|
|
mutex_unlock(vdev->lock);
|
|
return ret;
|
|
|
|
ioctl32_error:
|
|
mutex_unlock(vdev->lock);
|
|
SDEDEV_ERR(ctx->rot_dev->dev, "error handling ioctl32 cmd:%x\n", cmd);
|
|
return -EFAULT;
|
|
}
|
|
#endif /* CONFIG_COMPAT */
|
|
|
|
static int sde_rotator_ctrl_subscribe_event(struct v4l2_fh *fh,
|
|
const struct v4l2_event_subscription *sub)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int sde_rotator_event_unsubscribe(struct v4l2_fh *fh,
|
|
const struct v4l2_event_subscription *sub)
|
|
{
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* V4l2 ioctl handlers */
|
|
static const struct v4l2_ioctl_ops sde_rotator_ioctl_ops = {
|
|
.vidioc_querycap = sde_rotator_querycap,
|
|
.vidioc_enum_fmt_vid_out = sde_rotator_enum_fmt_vid_out,
|
|
.vidioc_enum_fmt_vid_cap = sde_rotator_enum_fmt_vid_cap,
|
|
.vidioc_g_fmt_vid_out = sde_rotator_g_fmt_out,
|
|
.vidioc_g_fmt_vid_cap = sde_rotator_g_fmt_cap,
|
|
.vidioc_try_fmt_vid_out = sde_rotator_try_fmt_vid_out,
|
|
.vidioc_try_fmt_vid_cap = sde_rotator_try_fmt_vid_cap,
|
|
.vidioc_s_fmt_vid_out = sde_rotator_s_fmt_vid_out,
|
|
.vidioc_s_fmt_vid_cap = sde_rotator_s_fmt_vid_cap,
|
|
.vidioc_reqbufs = sde_rotator_reqbufs,
|
|
.vidioc_qbuf = sde_rotator_qbuf,
|
|
.vidioc_dqbuf = sde_rotator_dqbuf,
|
|
.vidioc_querybuf = sde_rotator_querybuf,
|
|
.vidioc_streamon = sde_rotator_streamon,
|
|
.vidioc_streamoff = sde_rotator_streamoff,
|
|
.vidioc_g_selection = sde_rotator_g_selection,
|
|
.vidioc_s_selection = sde_rotator_s_selection,
|
|
.vidioc_g_parm = sde_rotator_g_parm,
|
|
.vidioc_s_parm = sde_rotator_s_parm,
|
|
.vidioc_default = sde_rotator_private_ioctl,
|
|
.vidioc_subscribe_event = sde_rotator_ctrl_subscribe_event,
|
|
.vidioc_unsubscribe_event = sde_rotator_event_unsubscribe,
|
|
};
|
|
|
|
/*
|
|
* sde_rotator_retire_handler - Invoked by hal when processing is done.
|
|
* @work: Pointer to work structure.
|
|
*
|
|
* This function is scheduled in work queue context.
|
|
*/
|
|
static void sde_rotator_retire_handler(struct kthread_work *work)
|
|
{
|
|
struct vb2_v4l2_buffer *src_buf;
|
|
struct vb2_v4l2_buffer *dst_buf;
|
|
struct sde_rotator_ctx *ctx;
|
|
struct sde_rotator_device *rot_dev;
|
|
struct sde_rotator_request *request;
|
|
|
|
request = container_of(work, struct sde_rotator_request, retire_work);
|
|
ctx = request->ctx;
|
|
|
|
if (!ctx || !ctx->rot_dev) {
|
|
SDEROT_ERR("null context/device\n");
|
|
return;
|
|
}
|
|
|
|
rot_dev = ctx->rot_dev;
|
|
|
|
SDEDEV_DBG(rot_dev->dev, "retire handler s:%d\n", ctx->session_id);
|
|
|
|
mutex_lock(&rot_dev->lock);
|
|
if (ctx->abort_pending) {
|
|
SDEDEV_DBG(rot_dev->dev, "abort command in retire s:%d\n",
|
|
ctx->session_id);
|
|
sde_rotator_update_retire_sequence(request);
|
|
sde_rotator_retire_request(request);
|
|
mutex_unlock(&rot_dev->lock);
|
|
return;
|
|
}
|
|
|
|
if (!ctx->file) {
|
|
sde_rotator_update_retire_sequence(request);
|
|
} else if (rot_dev->early_submit) {
|
|
if (IS_ERR_OR_NULL(request->req)) {
|
|
/* fail pending request or something wrong */
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"pending request fail in retire s:%d\n",
|
|
ctx->session_id);
|
|
}
|
|
|
|
/* pending request. reschedule this context. */
|
|
v4l2_m2m_try_schedule(ctx->fh.m2m_ctx);
|
|
} else {
|
|
/* no pending request. acknowledge the usual way. */
|
|
src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
|
|
dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
|
|
|
|
if (!src_buf || !dst_buf) {
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"null buffer in retire s:%d sb:%pK db:%pK\n",
|
|
ctx->session_id,
|
|
src_buf, dst_buf);
|
|
}
|
|
|
|
sde_rotator_update_retire_sequence(request);
|
|
sde_rotator_retire_request(request);
|
|
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
|
|
v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
|
|
v4l2_m2m_job_finish(rot_dev->m2m_dev, ctx->fh.m2m_ctx);
|
|
}
|
|
mutex_unlock(&rot_dev->lock);
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_process_buffers - Start rotator processing.
|
|
* @ctx: Pointer rotator context.
|
|
* @src_buf: Pointer to Vb2 source buffer.
|
|
* @dst_buf: Pointer to Vb2 destination buffer.
|
|
* @request: Pointer to rotator request
|
|
*/
|
|
static int sde_rotator_process_buffers(struct sde_rotator_ctx *ctx,
|
|
struct vb2_buffer *src_buf, struct vb2_buffer *dst_buf,
|
|
struct sde_rotator_request *request)
|
|
{
|
|
struct sde_rotator_device *rot_dev = ctx->rot_dev;
|
|
struct sde_rotation_item item;
|
|
struct sde_rot_entry_container *req = NULL;
|
|
struct sde_rotator_buf_handle *src_handle;
|
|
struct sde_rotator_buf_handle *dst_handle;
|
|
struct sde_rotator_statistics *stats = &rot_dev->stats;
|
|
struct sde_rotator_vbinfo *vbinfo_out;
|
|
struct sde_rotator_vbinfo *vbinfo_cap;
|
|
struct sde_rot_trace_entry rot_trace;
|
|
ktime_t *ts;
|
|
int ret;
|
|
|
|
if (!src_buf || !dst_buf) {
|
|
SDEDEV_ERR(rot_dev->dev, "null vb2 buffers\n");
|
|
ret = -EINVAL;
|
|
goto error_null_buffer;
|
|
}
|
|
|
|
src_handle = src_buf->planes[0].mem_priv;
|
|
dst_handle = dst_buf->planes[0].mem_priv;
|
|
|
|
if (!src_handle || !dst_handle) {
|
|
SDEDEV_ERR(rot_dev->dev, "null buffer handle\n");
|
|
ret = -EINVAL;
|
|
goto error_null_buffer;
|
|
}
|
|
|
|
vbinfo_out = &ctx->vbinfo_out[src_buf->index];
|
|
vbinfo_cap = &ctx->vbinfo_cap[dst_buf->index];
|
|
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"process buffer s:%d.%u src:(%u,%u,%u,%u) dst:(%u,%u,%u,%u) rot:%d flip:%d/%d sec:%d src_cr:%u/%u dst_cr:%u/%u\n",
|
|
ctx->session_id, vbinfo_cap->fence_ts,
|
|
ctx->crop_out.left, ctx->crop_out.top,
|
|
ctx->crop_out.width, ctx->crop_out.height,
|
|
ctx->crop_cap.left, ctx->crop_cap.top,
|
|
ctx->crop_cap.width, ctx->crop_cap.height,
|
|
ctx->rotate, ctx->hflip, ctx->vflip, ctx->secure,
|
|
vbinfo_out->comp_ratio.numer, vbinfo_out->comp_ratio.denom,
|
|
vbinfo_cap->comp_ratio.numer, vbinfo_cap->comp_ratio.denom);
|
|
|
|
/* allocate slot for timestamp */
|
|
ts = stats->ts[stats->count++ % SDE_ROTATOR_NUM_EVENTS];
|
|
ts[SDE_ROTATOR_TS_SRCQB] = vbinfo_out->qbuf_ts;
|
|
ts[SDE_ROTATOR_TS_DSTQB] = vbinfo_cap->qbuf_ts;
|
|
vbinfo_out->dqbuf_ts = &ts[SDE_ROTATOR_TS_SRCDQB];
|
|
vbinfo_cap->dqbuf_ts = &ts[SDE_ROTATOR_TS_DSTDQB];
|
|
|
|
ts[SDE_ROTATOR_TS_FENCE] = ktime_get();
|
|
|
|
/* Set values to pass to trace */
|
|
rot_trace.wb_idx = ctx->fh.prio;
|
|
rot_trace.flags = (ctx->rotate << 0) | (ctx->hflip << 8) |
|
|
(ctx->hflip << 9) | (ctx->secure << 10);
|
|
rot_trace.input_format = ctx->format_out.fmt.pix.pixelformat;
|
|
rot_trace.input_width = ctx->format_out.fmt.pix.width;
|
|
rot_trace.input_height = ctx->format_out.fmt.pix.height;
|
|
rot_trace.src_x = ctx->crop_out.left;
|
|
rot_trace.src_y = ctx->crop_out.top;
|
|
rot_trace.src_w = ctx->crop_out.width;
|
|
rot_trace.src_h = ctx->crop_out.height;
|
|
rot_trace.output_format = ctx->format_cap.fmt.pix.pixelformat;
|
|
rot_trace.output_width = ctx->format_cap.fmt.pix.width;
|
|
rot_trace.output_height = ctx->format_cap.fmt.pix.height;
|
|
rot_trace.dst_x = ctx->crop_cap.left;
|
|
rot_trace.dst_y = ctx->crop_cap.top;
|
|
rot_trace.dst_w = ctx->crop_cap.width;
|
|
rot_trace.dst_h = ctx->crop_cap.height;
|
|
|
|
trace_rot_entry_fence(
|
|
ctx->session_id, vbinfo_cap->fence_ts, &rot_trace);
|
|
|
|
if (vbinfo_out->fence) {
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
mutex_unlock(&rot_dev->lock);
|
|
SDEDEV_DBG(rot_dev->dev, "fence enter s:%d.%d fd:%d\n",
|
|
ctx->session_id, vbinfo_cap->fence_ts, vbinfo_out->fd);
|
|
ret = sde_rotator_wait_sync_fence(vbinfo_out->fence,
|
|
rot_dev->fence_timeout);
|
|
mutex_lock(&rot_dev->lock);
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
sde_rotator_put_sync_fence(vbinfo_out->fence);
|
|
vbinfo_out->fence = NULL;
|
|
if (ret) {
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"error waiting for fence s:%d.%d fd:%d r:%d\n",
|
|
ctx->session_id,
|
|
vbinfo_cap->fence_ts, vbinfo_out->fd, ret);
|
|
SDEROT_EVTLOG(ctx->session_id, vbinfo_cap->fence_ts,
|
|
vbinfo_out->fd, ret,
|
|
SDE_ROT_EVTLOG_ERROR);
|
|
goto error_fence_wait;
|
|
} else {
|
|
SDEDEV_DBG(rot_dev->dev, "fence exit s:%d.%d fd:%d\n",
|
|
ctx->session_id,
|
|
vbinfo_cap->fence_ts, vbinfo_out->fd);
|
|
}
|
|
}
|
|
|
|
/* fill in item work structure */
|
|
sde_rotator_get_item_from_ctx(ctx, &item);
|
|
item.flags |= SDE_ROTATION_EXT_DMA_BUF;
|
|
item.input.planes[0].fd = src_handle->fd;
|
|
item.input.planes[0].buffer = src_handle->buffer;
|
|
item.input.planes[0].offset = src_handle->addr;
|
|
item.input.planes[0].stride = ctx->format_out.fmt.pix.bytesperline;
|
|
item.input.plane_count = 1;
|
|
item.input.fence = NULL;
|
|
item.input.comp_ratio = vbinfo_out->comp_ratio;
|
|
item.output.planes[0].fd = dst_handle->fd;
|
|
item.output.planes[0].buffer = dst_handle->buffer;
|
|
item.output.planes[0].offset = dst_handle->addr;
|
|
item.output.planes[0].stride = ctx->format_cap.fmt.pix.bytesperline;
|
|
item.output.plane_count = 1;
|
|
item.output.fence = NULL;
|
|
item.output.comp_ratio = vbinfo_cap->comp_ratio;
|
|
item.sequence_id = vbinfo_cap->fence_ts;
|
|
item.ts = ts;
|
|
|
|
req = sde_rotator_req_init(rot_dev->mgr, ctx->private, &item, 1, 0);
|
|
if (IS_ERR_OR_NULL(req)) {
|
|
SDEDEV_ERR(rot_dev->dev, "fail allocate rotation request\n");
|
|
ret = -ENOMEM;
|
|
goto error_init_request;
|
|
}
|
|
|
|
req->retire_kw = ctx->work_queue.rot_kw;
|
|
req->retire_work = &request->retire_work;
|
|
|
|
ret = sde_rotator_handle_request_common(
|
|
rot_dev->mgr, ctx->private, req);
|
|
if (ret) {
|
|
SDEDEV_ERR(rot_dev->dev, "fail handle request\n");
|
|
goto error_handle_request;
|
|
}
|
|
|
|
sde_rotator_queue_request(rot_dev->mgr, ctx->private, req);
|
|
request->req = req;
|
|
request->sequence_id = item.sequence_id;
|
|
request->committed = true;
|
|
|
|
return 0;
|
|
error_handle_request:
|
|
devm_kfree(rot_dev->dev, req);
|
|
error_init_request:
|
|
error_fence_wait:
|
|
error_null_buffer:
|
|
request->req = NULL;
|
|
request->sequence_id = 0;
|
|
request->committed = false;
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_submit_handler - Invoked by m2m to submit job.
|
|
* @work: Pointer to work structure.
|
|
*
|
|
* This function is scheduled in work queue context.
|
|
*/
|
|
static void sde_rotator_submit_handler(struct kthread_work *work)
|
|
{
|
|
struct sde_rotator_ctx *ctx;
|
|
struct sde_rotator_device *rot_dev;
|
|
struct vb2_v4l2_buffer *src_buf;
|
|
struct vb2_v4l2_buffer *dst_buf;
|
|
struct sde_rotator_request *request;
|
|
int ret;
|
|
|
|
request = container_of(work, struct sde_rotator_request, submit_work);
|
|
ctx = request->ctx;
|
|
|
|
if (!ctx || !ctx->rot_dev) {
|
|
SDEROT_ERR("null device\n");
|
|
return;
|
|
}
|
|
|
|
rot_dev = ctx->rot_dev;
|
|
SDEDEV_DBG(rot_dev->dev, "submit handler s:%d\n", ctx->session_id);
|
|
|
|
mutex_lock(&rot_dev->lock);
|
|
if (ctx->abort_pending) {
|
|
SDEDEV_DBG(rot_dev->dev, "abort command in submit s:%d\n",
|
|
ctx->session_id);
|
|
sde_rotator_update_retire_sequence(request);
|
|
sde_rotator_retire_request(request);
|
|
mutex_unlock(&rot_dev->lock);
|
|
return;
|
|
}
|
|
|
|
/* submit new request */
|
|
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
|
|
src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
ret = sde_rotator_process_buffers(ctx, &src_buf->vb2_buf,
|
|
&dst_buf->vb2_buf, request);
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
if (ret) {
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"fail process buffer in submit s:%d\n",
|
|
ctx->session_id);
|
|
/* advance to device run to clean up buffers */
|
|
v4l2_m2m_try_schedule(ctx->fh.m2m_ctx);
|
|
}
|
|
|
|
mutex_unlock(&rot_dev->lock);
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_device_run - rotator m2m device run callback
|
|
* @priv: Pointer rotator context.
|
|
*/
|
|
static void sde_rotator_device_run(void *priv)
|
|
{
|
|
struct sde_rotator_ctx *ctx = priv;
|
|
struct sde_rotator_device *rot_dev;
|
|
struct vb2_v4l2_buffer *src_buf;
|
|
struct vb2_v4l2_buffer *dst_buf;
|
|
struct sde_rotator_request *request;
|
|
int ret;
|
|
|
|
if (!ctx || !ctx->rot_dev) {
|
|
SDEROT_ERR("null context/device\n");
|
|
return;
|
|
}
|
|
|
|
rot_dev = ctx->rot_dev;
|
|
SDEDEV_DBG(rot_dev->dev, "device run s:%d\n", ctx->session_id);
|
|
|
|
if (rot_dev->early_submit) {
|
|
request = list_first_entry_or_null(&ctx->pending_list,
|
|
struct sde_rotator_request, list);
|
|
|
|
/* pending request mode, check for completion */
|
|
if (!request || IS_ERR_OR_NULL(request->req)) {
|
|
/* pending request fails or something wrong. */
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"pending request fail in device run s:%d\n",
|
|
ctx->session_id);
|
|
rot_dev->stats.fail_count++;
|
|
ATRACE_INT("fail_count", rot_dev->stats.fail_count);
|
|
goto error_process_buffers;
|
|
|
|
} else if (!atomic_read(&request->req->pending_count)) {
|
|
/* pending request completed. signal done. */
|
|
int failed_count =
|
|
atomic_read(&request->req->failed_count);
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"pending request completed in device run s:%d\n",
|
|
ctx->session_id);
|
|
|
|
/* disconnect request (will be freed by core layer) */
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
sde_rotator_req_finish(rot_dev->mgr, ctx->private,
|
|
request->req);
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
|
|
if (failed_count) {
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"pending request failed in device run s:%d f:%d\n",
|
|
ctx->session_id,
|
|
failed_count);
|
|
rot_dev->stats.fail_count++;
|
|
ATRACE_INT("fail_count",
|
|
rot_dev->stats.fail_count);
|
|
goto error_process_buffers;
|
|
}
|
|
|
|
src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
|
|
dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
|
|
if (!src_buf || !dst_buf) {
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"null buffer in device run s:%d sb:%pK db:%pK\n",
|
|
ctx->session_id,
|
|
src_buf, dst_buf);
|
|
goto error_process_buffers;
|
|
}
|
|
|
|
sde_rotator_update_retire_sequence(request);
|
|
sde_rotator_retire_request(request);
|
|
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_DONE);
|
|
v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_DONE);
|
|
v4l2_m2m_job_finish(rot_dev->m2m_dev, ctx->fh.m2m_ctx);
|
|
} else {
|
|
/* pending request not complete. something wrong. */
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"Incomplete pending request in device run s:%d\n",
|
|
ctx->session_id);
|
|
|
|
/* disconnect request (will be freed by core layer) */
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
sde_rotator_req_finish(rot_dev->mgr, ctx->private,
|
|
request->req);
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
|
|
goto error_process_buffers;
|
|
}
|
|
} else {
|
|
request = list_first_entry_or_null(&ctx->retired_list,
|
|
struct sde_rotator_request, list);
|
|
if (!request) {
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"no free request in device run s:%d\n",
|
|
ctx->session_id);
|
|
goto error_retired_list;
|
|
}
|
|
|
|
spin_lock(&ctx->list_lock);
|
|
list_del_init(&request->list);
|
|
list_add_tail(&request->list, &ctx->pending_list);
|
|
spin_unlock(&ctx->list_lock);
|
|
|
|
/* no pending request. submit buffer the usual way. */
|
|
dst_buf = v4l2_m2m_next_dst_buf(ctx->fh.m2m_ctx);
|
|
src_buf = v4l2_m2m_next_src_buf(ctx->fh.m2m_ctx);
|
|
if (!src_buf || !dst_buf) {
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"null buffer in device run s:%d sb:%pK db:%pK\n",
|
|
ctx->session_id,
|
|
src_buf, dst_buf);
|
|
goto error_empty_buffer;
|
|
}
|
|
|
|
sde_rot_mgr_lock(rot_dev->mgr);
|
|
ret = sde_rotator_process_buffers(ctx, &src_buf->vb2_buf,
|
|
&dst_buf->vb2_buf, request);
|
|
sde_rot_mgr_unlock(rot_dev->mgr);
|
|
if (ret) {
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"fail process buffer in device run s:%d\n",
|
|
ctx->session_id);
|
|
rot_dev->stats.fail_count++;
|
|
ATRACE_INT("fail_count", rot_dev->stats.fail_count);
|
|
goto error_process_buffers;
|
|
}
|
|
}
|
|
|
|
return;
|
|
error_process_buffers:
|
|
error_empty_buffer:
|
|
error_retired_list:
|
|
sde_rotator_update_retire_sequence(request);
|
|
sde_rotator_retire_request(request);
|
|
src_buf = v4l2_m2m_src_buf_remove(ctx->fh.m2m_ctx);
|
|
dst_buf = v4l2_m2m_dst_buf_remove(ctx->fh.m2m_ctx);
|
|
if (src_buf)
|
|
v4l2_m2m_buf_done(src_buf, VB2_BUF_STATE_ERROR);
|
|
if (dst_buf)
|
|
v4l2_m2m_buf_done(dst_buf, VB2_BUF_STATE_ERROR);
|
|
sde_rotator_resync_timeline(ctx->work_queue.timeline);
|
|
v4l2_m2m_job_finish(rot_dev->m2m_dev, ctx->fh.m2m_ctx);
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_job_abort - rotator m2m job abort callback
|
|
* @priv: Pointer rotator context.
|
|
*/
|
|
static void sde_rotator_job_abort(void *priv)
|
|
{
|
|
struct sde_rotator_ctx *ctx = priv;
|
|
struct sde_rotator_device *rot_dev;
|
|
|
|
if (!ctx || !ctx->rot_dev) {
|
|
SDEROT_ERR("null context/device\n");
|
|
return;
|
|
}
|
|
|
|
rot_dev = ctx->rot_dev;
|
|
SDEDEV_DBG(rot_dev->dev, "job abort s:%d\n", ctx->session_id);
|
|
|
|
v4l2_m2m_job_finish(rot_dev->m2m_dev, ctx->fh.m2m_ctx);
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_job_ready - rotator m2m job ready callback
|
|
* @priv: Pointer rotator context.
|
|
*/
|
|
static int sde_rotator_job_ready(void *priv)
|
|
{
|
|
struct sde_rotator_ctx *ctx = priv;
|
|
struct sde_rotator_device *rot_dev;
|
|
struct sde_rotator_request *request;
|
|
int ret = 0;
|
|
|
|
if (!ctx || !ctx->rot_dev) {
|
|
SDEROT_ERR("null context/device\n");
|
|
return 0;
|
|
}
|
|
|
|
rot_dev = ctx->rot_dev;
|
|
SDEDEV_DBG(rot_dev->dev, "job ready s:%d\n", ctx->session_id);
|
|
|
|
request = list_first_entry_or_null(&ctx->pending_list,
|
|
struct sde_rotator_request, list);
|
|
|
|
if (!rot_dev->early_submit) {
|
|
/* always ready in normal mode. */
|
|
ret = 1;
|
|
} else if (request && IS_ERR_OR_NULL(request->req)) {
|
|
/* if pending request fails, forward to device run state. */
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"pending request fail in job ready s:%d\n",
|
|
ctx->session_id);
|
|
ret = 1;
|
|
} else if (list_empty(&ctx->pending_list)) {
|
|
/* if no pending request, submit a new request. */
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"submit job s:%d sc:%d dc:%d p:%d\n",
|
|
ctx->session_id,
|
|
v4l2_m2m_num_src_bufs_ready(ctx->fh.m2m_ctx),
|
|
v4l2_m2m_num_dst_bufs_ready(ctx->fh.m2m_ctx),
|
|
!list_empty(&ctx->pending_list));
|
|
|
|
request = list_first_entry_or_null(&ctx->retired_list,
|
|
struct sde_rotator_request, list);
|
|
if (!request) {
|
|
/* should not happen */
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"no free request in job ready s:%d\n",
|
|
ctx->session_id);
|
|
} else {
|
|
spin_lock(&ctx->list_lock);
|
|
list_del_init(&request->list);
|
|
list_add_tail(&request->list, &ctx->pending_list);
|
|
spin_unlock(&ctx->list_lock);
|
|
kthread_queue_work(ctx->work_queue.rot_kw,
|
|
&request->submit_work);
|
|
}
|
|
} else if (request && !atomic_read(&request->req->pending_count)) {
|
|
/* if pending request completed, forward to device run state */
|
|
SDEDEV_DBG(rot_dev->dev,
|
|
"pending request completed in job ready s:%d\n",
|
|
ctx->session_id);
|
|
ret = 1;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* V4l2 mem2mem handlers */
|
|
static struct v4l2_m2m_ops sde_rotator_m2m_ops = {
|
|
.device_run = sde_rotator_device_run,
|
|
.job_abort = sde_rotator_job_abort,
|
|
.job_ready = sde_rotator_job_ready,
|
|
};
|
|
#endif
|
|
|
|
/* Device tree match struct */
|
|
static const struct of_device_id sde_rotator_dt_match[] = {
|
|
{
|
|
.compatible = "qcom,sde_rotator",
|
|
.data = NULL,
|
|
},
|
|
{}
|
|
};
|
|
|
|
#ifndef CONFIG_MSM_SDE_ROTATOR_INIT_ONLY
|
|
/*
|
|
* sde_rotator_get_drv_data - rotator device driver data.
|
|
* @dev: Pointer to device.
|
|
*/
|
|
static const void *sde_rotator_get_drv_data(struct device *dev)
|
|
{
|
|
const struct of_device_id *match;
|
|
|
|
match = of_match_node(sde_rotator_dt_match, dev->of_node);
|
|
|
|
if (match)
|
|
return match->data;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_probe - rotator device probe method.
|
|
* @pdev: Pointer to rotator platform device.
|
|
*/
|
|
static int sde_rotator_probe(struct platform_device *pdev)
|
|
{
|
|
struct sde_rotator_device *rot_dev;
|
|
struct video_device *vdev;
|
|
int ret, i;
|
|
char name[32];
|
|
|
|
SDEDEV_DBG(&pdev->dev, "SDE v4l2 rotator probed\n");
|
|
|
|
/* sde rotator device struct */
|
|
rot_dev = kzalloc(sizeof(struct sde_rotator_device), GFP_KERNEL);
|
|
if (!rot_dev)
|
|
return -ENOMEM;
|
|
|
|
mutex_init(&rot_dev->lock);
|
|
rot_dev->early_submit = SDE_ROTATOR_EARLY_SUBMIT;
|
|
rot_dev->fence_timeout = SDE_ROTATOR_FENCE_TIMEOUT;
|
|
rot_dev->streamoff_timeout = SDE_ROTATOR_STREAM_OFF_TIMEOUT;
|
|
rot_dev->min_rot_clk = 0;
|
|
rot_dev->min_bw = 0;
|
|
rot_dev->min_overhead_us = 0;
|
|
rot_dev->drvdata = sde_rotator_get_drv_data(&pdev->dev);
|
|
rot_dev->open_timeout = SDE_ROTATOR_CTX_OPEN_TIMEOUT;
|
|
init_waitqueue_head(&rot_dev->open_wq);
|
|
|
|
rot_dev->pdev = pdev;
|
|
rot_dev->dev = &pdev->dev;
|
|
platform_set_drvdata(pdev, rot_dev);
|
|
|
|
ret = sde_rotator_base_init(&rot_dev->mdata, pdev, rot_dev->drvdata);
|
|
if (ret < 0) {
|
|
SDEDEV_ERR(&pdev->dev, "fail init base data %d\n", ret);
|
|
goto error_rotator_base_init;
|
|
}
|
|
|
|
ret = sde_rotator_core_init(&rot_dev->mgr, pdev);
|
|
if (ret < 0) {
|
|
if (ret == -EPROBE_DEFER)
|
|
SDEDEV_INFO(&pdev->dev, "probe defer for core init\n");
|
|
else
|
|
SDEDEV_ERR(&pdev->dev, "fail init core %d\n", ret);
|
|
goto error_rotator_core_init;
|
|
}
|
|
|
|
/* mem2mem device */
|
|
rot_dev->m2m_dev = v4l2_m2m_init(&sde_rotator_m2m_ops);
|
|
if (IS_ERR(rot_dev->m2m_dev)) {
|
|
ret = PTR_ERR(rot_dev->m2m_dev);
|
|
SDEDEV_ERR(&pdev->dev, "fail init mem2mem device %d\n", ret);
|
|
goto error_m2m_init;
|
|
}
|
|
|
|
/* v4l2 device */
|
|
ret = v4l2_device_register(&pdev->dev, &rot_dev->v4l2_dev);
|
|
if (ret < 0) {
|
|
SDEDEV_ERR(&pdev->dev, "fail register v4l2 device %d\n", ret);
|
|
goto error_v4l2_register;
|
|
}
|
|
|
|
vdev = video_device_alloc();
|
|
if (!vdev) {
|
|
SDEDEV_ERR(&pdev->dev, "fail allocate video device\n");
|
|
goto error_alloc_video_device;
|
|
}
|
|
|
|
vdev->fops = &sde_rotator_fops;
|
|
vdev->ioctl_ops = &sde_rotator_ioctl_ops;
|
|
vdev->lock = &rot_dev->lock;
|
|
vdev->minor = -1;
|
|
vdev->release = video_device_release;
|
|
vdev->v4l2_dev = &rot_dev->v4l2_dev;
|
|
vdev->vfl_dir = VFL_DIR_M2M;
|
|
vdev->vfl_type = VFL_TYPE_VIDEO;
|
|
vdev->device_caps = V4L2_CAP_STREAMING | V4L2_CAP_VIDEO_M2M |
|
|
V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_CAPTURE;
|
|
|
|
strlcpy(vdev->name, SDE_ROTATOR_DRV_NAME, sizeof(vdev->name));
|
|
|
|
ret = video_register_device(vdev, VFL_TYPE_VIDEO,
|
|
SDE_ROTATOR_BASE_DEVICE_NUMBER);
|
|
if (ret < 0) {
|
|
SDEDEV_ERR(&pdev->dev, "fail register video device %d\n",
|
|
ret);
|
|
goto error_video_register;
|
|
}
|
|
|
|
rot_dev->vdev = vdev;
|
|
video_set_drvdata(rot_dev->vdev, rot_dev);
|
|
|
|
rot_dev->debugfs_root = sde_rotator_create_debugfs(rot_dev);
|
|
|
|
for (i = 0; i < MAX_ROT_OPEN_SESSION; i++) {
|
|
snprintf(name, sizeof(name), "rot_fenceq_%d_%d",
|
|
rot_dev->dev->id, i);
|
|
kthread_init_worker(&rot_dev->rot_kw[i]);
|
|
rot_dev->rot_thread[i] = kthread_run(kthread_worker_fn,
|
|
&rot_dev->rot_kw[i], name);
|
|
if (IS_ERR(rot_dev->rot_thread[i])) {
|
|
SDEDEV_ERR(rot_dev->dev,
|
|
"fail allocate kthread i:%d\n", i);
|
|
ret = -EPERM;
|
|
goto error_kthread_create;
|
|
}
|
|
rot_dev->kthread_free[i] = true;
|
|
}
|
|
|
|
SDEDEV_INFO(&pdev->dev, "SDE v4l2 rotator probe success\n");
|
|
|
|
return 0;
|
|
error_kthread_create:
|
|
for (i--; i >= 0; i--)
|
|
kthread_stop(rot_dev->rot_thread[i]);
|
|
sde_rotator_destroy_debugfs(rot_dev->debugfs_root);
|
|
video_unregister_device(rot_dev->vdev);
|
|
error_video_register:
|
|
video_device_release(vdev);
|
|
error_alloc_video_device:
|
|
v4l2_device_unregister(&rot_dev->v4l2_dev);
|
|
error_v4l2_register:
|
|
v4l2_m2m_release(rot_dev->m2m_dev);
|
|
error_m2m_init:
|
|
sde_rotator_core_destroy(rot_dev->mgr);
|
|
error_rotator_core_init:
|
|
sde_rotator_base_destroy(rot_dev->mdata);
|
|
error_rotator_base_init:
|
|
kfree(rot_dev);
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_remove - rotator device remove method.
|
|
* @pdev: Pointer rotator platform device.
|
|
*/
|
|
static int sde_rotator_remove(struct platform_device *pdev)
|
|
{
|
|
struct sde_rotator_device *rot_dev;
|
|
int i;
|
|
|
|
rot_dev = platform_get_drvdata(pdev);
|
|
if (rot_dev == NULL) {
|
|
SDEDEV_ERR(&pdev->dev, "fail get rotator drvdata\n");
|
|
return 0;
|
|
}
|
|
|
|
for (i = MAX_ROT_OPEN_SESSION - 1; i >= 0; i--)
|
|
kthread_stop(rot_dev->rot_thread[i]);
|
|
sde_rotator_destroy_debugfs(rot_dev->debugfs_root);
|
|
video_unregister_device(rot_dev->vdev);
|
|
video_device_release(rot_dev->vdev);
|
|
v4l2_device_unregister(&rot_dev->v4l2_dev);
|
|
v4l2_m2m_release(rot_dev->m2m_dev);
|
|
sde_rotator_core_destroy(rot_dev->mgr);
|
|
sde_rotator_base_destroy(rot_dev->mdata);
|
|
kfree(rot_dev);
|
|
return 0;
|
|
}
|
|
|
|
static const struct dev_pm_ops sde_rotator_pm_ops = {
|
|
SET_SYSTEM_SLEEP_PM_OPS(sde_rotator_pm_suspend, sde_rotator_pm_resume)
|
|
SET_RUNTIME_PM_OPS(sde_rotator_runtime_suspend,
|
|
sde_rotator_runtime_resume,
|
|
sde_rotator_runtime_idle)
|
|
};
|
|
|
|
/* SDE Rotator platform driver definition */
|
|
static struct platform_driver rotator_driver = {
|
|
.probe = sde_rotator_probe,
|
|
.remove = sde_rotator_remove,
|
|
.suspend = sde_rotator_suspend,
|
|
.resume = sde_rotator_resume,
|
|
.driver = {
|
|
.name = SDE_ROTATOR_DRV_NAME,
|
|
.of_match_table = sde_rotator_dt_match,
|
|
.pm = &sde_rotator_pm_ops,
|
|
},
|
|
};
|
|
|
|
#else
|
|
/*
|
|
* sde_rotator_probe - rotator device probe method.
|
|
* @pdev: Pointer to rotator platform device.
|
|
*/
|
|
static int sde_rotator_probe(struct platform_device *pdev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* sde_rotator_remove - rotator device remove method.
|
|
* @pdev: Pointer rotator platform device.
|
|
*/
|
|
static int sde_rotator_remove(struct platform_device *pdev)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
/* SDE Rotator platform driver definition */
|
|
static struct platform_driver rotator_driver = {
|
|
.probe = sde_rotator_probe,
|
|
.remove = sde_rotator_remove,
|
|
.driver = {
|
|
.name = SDE_ROTATOR_DRV_NAME,
|
|
.of_match_table = sde_rotator_dt_match,
|
|
},
|
|
};
|
|
#endif
|
|
|
|
void sde_rotator_register(void)
|
|
{
|
|
platform_driver_register(&rotator_driver);
|
|
}
|
|
|
|
void sde_rotator_unregister(void)
|
|
{
|
|
platform_driver_unregister(&rotator_driver);
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(5, 19, 0))
|
|
MODULE_IMPORT_NS(DMA_BUF);
|
|
#endif
|