diff options
author | Mauro Carvalho Chehab <mchehab@kernel.org> | 2022-03-13 11:18:18 +0100 |
---|---|---|
committer | Mauro Carvalho Chehab <mchehab@kernel.org> | 2022-03-18 05:58:34 +0100 |
commit | 43ecec16c4face9a59e81771e7cbff4671c62117 (patch) | |
tree | ca6f40fedfd661743ee9819d2fe861770d656a91 /drivers/media/platform/s5p-mfc/s5p_mfc.c | |
parent | media: platform: rename s5p-jpeg/ to samsung/s5p-jpeg/ (diff) | |
download | linux-dev-43ecec16c4face9a59e81771e7cbff4671c62117.tar.xz linux-dev-43ecec16c4face9a59e81771e7cbff4671c62117.zip |
media: platform: rename s5p-mfc/ to samsung/s5p-mfc/
As the end goal is to have platform drivers split by vendor,
rename s5p-mfc/ to samsung/s5p-mfc/.
Signed-off-by: Mauro Carvalho Chehab <mchehab@kernel.org>
Diffstat (limited to 'drivers/media/platform/s5p-mfc/s5p_mfc.c')
-rw-r--r-- | drivers/media/platform/s5p-mfc/s5p_mfc.c | 1680 |
1 files changed, 0 insertions, 1680 deletions
diff --git a/drivers/media/platform/s5p-mfc/s5p_mfc.c b/drivers/media/platform/s5p-mfc/s5p_mfc.c deleted file mode 100644 index 761341934925..000000000000 --- a/drivers/media/platform/s5p-mfc/s5p_mfc.c +++ /dev/null @@ -1,1680 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * Samsung S5P Multi Format Codec v 5.1 - * - * Copyright (c) 2011 Samsung Electronics Co., Ltd. - * Kamil Debski, <k.debski@samsung.com> - */ - -#include <linux/clk.h> -#include <linux/delay.h> -#include <linux/interrupt.h> -#include <linux/io.h> -#include <linux/module.h> -#include <linux/platform_device.h> -#include <linux/sched.h> -#include <linux/slab.h> -#include <linux/videodev2.h> -#include <media/v4l2-event.h> -#include <linux/workqueue.h> -#include <linux/of.h> -#include <linux/of_device.h> -#include <linux/of_reserved_mem.h> -#include <media/videobuf2-v4l2.h> -#include "s5p_mfc_common.h" -#include "s5p_mfc_ctrl.h" -#include "s5p_mfc_debug.h" -#include "s5p_mfc_dec.h" -#include "s5p_mfc_enc.h" -#include "s5p_mfc_intr.h" -#include "s5p_mfc_iommu.h" -#include "s5p_mfc_opr.h" -#include "s5p_mfc_cmd.h" -#include "s5p_mfc_pm.h" - -#define S5P_MFC_DEC_NAME "s5p-mfc-dec" -#define S5P_MFC_ENC_NAME "s5p-mfc-enc" - -int mfc_debug_level; -module_param_named(debug, mfc_debug_level, int, S_IRUGO | S_IWUSR); -MODULE_PARM_DESC(debug, "Debug level - higher value produces more verbose messages"); - -static char *mfc_mem_size; -module_param_named(mem, mfc_mem_size, charp, 0644); -MODULE_PARM_DESC(mem, "Preallocated memory size for the firmware and context buffers"); - -/* Helper functions for interrupt processing */ - -/* Remove from hw execution round robin */ -void clear_work_bit(struct s5p_mfc_ctx *ctx) -{ - struct s5p_mfc_dev *dev = ctx->dev; - - spin_lock(&dev->condlock); - __clear_bit(ctx->num, &dev->ctx_work_bits); - spin_unlock(&dev->condlock); -} - -/* Add to hw execution round robin */ -void set_work_bit(struct s5p_mfc_ctx *ctx) -{ - struct s5p_mfc_dev *dev = ctx->dev; - - spin_lock(&dev->condlock); - __set_bit(ctx->num, &dev->ctx_work_bits); - spin_unlock(&dev->condlock); -} - -/* Remove from hw execution round robin */ -void clear_work_bit_irqsave(struct s5p_mfc_ctx *ctx) -{ - struct s5p_mfc_dev *dev = ctx->dev; - unsigned long flags; - - spin_lock_irqsave(&dev->condlock, flags); - __clear_bit(ctx->num, &dev->ctx_work_bits); - spin_unlock_irqrestore(&dev->condlock, flags); -} - -/* Add to hw execution round robin */ -void set_work_bit_irqsave(struct s5p_mfc_ctx *ctx) -{ - struct s5p_mfc_dev *dev = ctx->dev; - unsigned long flags; - - spin_lock_irqsave(&dev->condlock, flags); - __set_bit(ctx->num, &dev->ctx_work_bits); - spin_unlock_irqrestore(&dev->condlock, flags); -} - -int s5p_mfc_get_new_ctx(struct s5p_mfc_dev *dev) -{ - unsigned long flags; - int ctx; - - spin_lock_irqsave(&dev->condlock, flags); - ctx = dev->curr_ctx; - do { - ctx = (ctx + 1) % MFC_NUM_CONTEXTS; - if (ctx == dev->curr_ctx) { - if (!test_bit(ctx, &dev->ctx_work_bits)) - ctx = -EAGAIN; - break; - } - } while (!test_bit(ctx, &dev->ctx_work_bits)); - spin_unlock_irqrestore(&dev->condlock, flags); - - return ctx; -} - -/* Wake up context wait_queue */ -static void wake_up_ctx(struct s5p_mfc_ctx *ctx, unsigned int reason, - unsigned int err) -{ - ctx->int_cond = 1; - ctx->int_type = reason; - ctx->int_err = err; - wake_up(&ctx->queue); -} - -/* Wake up device wait_queue */ -static void wake_up_dev(struct s5p_mfc_dev *dev, unsigned int reason, - unsigned int err) -{ - dev->int_cond = 1; - dev->int_type = reason; - dev->int_err = err; - wake_up(&dev->queue); -} - -void s5p_mfc_cleanup_queue(struct list_head *lh, struct vb2_queue *vq) -{ - struct s5p_mfc_buf *b; - int i; - - while (!list_empty(lh)) { - b = list_entry(lh->next, struct s5p_mfc_buf, list); - for (i = 0; i < b->b->vb2_buf.num_planes; i++) - vb2_set_plane_payload(&b->b->vb2_buf, i, 0); - vb2_buffer_done(&b->b->vb2_buf, VB2_BUF_STATE_ERROR); - list_del(&b->list); - } -} - -static void s5p_mfc_watchdog(struct timer_list *t) -{ - struct s5p_mfc_dev *dev = from_timer(dev, t, watchdog_timer); - - if (test_bit(0, &dev->hw_lock)) - atomic_inc(&dev->watchdog_cnt); - if (atomic_read(&dev->watchdog_cnt) >= MFC_WATCHDOG_CNT) { - /* This means that hw is busy and no interrupts were - * generated by hw for the Nth time of running this - * watchdog timer. This usually means a serious hw - * error. Now it is time to kill all instances and - * reset the MFC. */ - mfc_err("Time out during waiting for HW\n"); - schedule_work(&dev->watchdog_work); - } - dev->watchdog_timer.expires = jiffies + - msecs_to_jiffies(MFC_WATCHDOG_INTERVAL); - add_timer(&dev->watchdog_timer); -} - -static void s5p_mfc_watchdog_worker(struct work_struct *work) -{ - struct s5p_mfc_dev *dev; - struct s5p_mfc_ctx *ctx; - unsigned long flags; - int mutex_locked; - int i, ret; - - dev = container_of(work, struct s5p_mfc_dev, watchdog_work); - - mfc_err("Driver timeout error handling\n"); - /* Lock the mutex that protects open and release. - * This is necessary as they may load and unload firmware. */ - mutex_locked = mutex_trylock(&dev->mfc_mutex); - if (!mutex_locked) - mfc_err("Error: some instance may be closing/opening\n"); - spin_lock_irqsave(&dev->irqlock, flags); - - s5p_mfc_clock_off(); - - for (i = 0; i < MFC_NUM_CONTEXTS; i++) { - ctx = dev->ctx[i]; - if (!ctx) - continue; - ctx->state = MFCINST_ERROR; - s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst); - s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src); - clear_work_bit(ctx); - wake_up_ctx(ctx, S5P_MFC_R2H_CMD_ERR_RET, 0); - } - clear_bit(0, &dev->hw_lock); - spin_unlock_irqrestore(&dev->irqlock, flags); - - /* De-init MFC */ - s5p_mfc_deinit_hw(dev); - - /* Double check if there is at least one instance running. - * If no instance is in memory than no firmware should be present */ - if (dev->num_inst > 0) { - ret = s5p_mfc_load_firmware(dev); - if (ret) { - mfc_err("Failed to reload FW\n"); - goto unlock; - } - s5p_mfc_clock_on(); - ret = s5p_mfc_init_hw(dev); - s5p_mfc_clock_off(); - if (ret) - mfc_err("Failed to reinit FW\n"); - } -unlock: - if (mutex_locked) - mutex_unlock(&dev->mfc_mutex); -} - -static void s5p_mfc_handle_frame_all_extracted(struct s5p_mfc_ctx *ctx) -{ - struct s5p_mfc_buf *dst_buf; - struct s5p_mfc_dev *dev = ctx->dev; - - ctx->state = MFCINST_FINISHED; - ctx->sequence++; - while (!list_empty(&ctx->dst_queue)) { - dst_buf = list_entry(ctx->dst_queue.next, - struct s5p_mfc_buf, list); - mfc_debug(2, "Cleaning up buffer: %d\n", - dst_buf->b->vb2_buf.index); - vb2_set_plane_payload(&dst_buf->b->vb2_buf, 0, 0); - vb2_set_plane_payload(&dst_buf->b->vb2_buf, 1, 0); - list_del(&dst_buf->list); - dst_buf->flags |= MFC_BUF_FLAG_EOS; - ctx->dst_queue_cnt--; - dst_buf->b->sequence = (ctx->sequence++); - - if (s5p_mfc_hw_call(dev->mfc_ops, get_pic_type_top, ctx) == - s5p_mfc_hw_call(dev->mfc_ops, get_pic_type_bot, ctx)) - dst_buf->b->field = V4L2_FIELD_NONE; - else - dst_buf->b->field = V4L2_FIELD_INTERLACED; - dst_buf->b->flags |= V4L2_BUF_FLAG_LAST; - - ctx->dec_dst_flag &= ~(1 << dst_buf->b->vb2_buf.index); - vb2_buffer_done(&dst_buf->b->vb2_buf, VB2_BUF_STATE_DONE); - } -} - -static void s5p_mfc_handle_frame_copy_time(struct s5p_mfc_ctx *ctx) -{ - struct s5p_mfc_dev *dev = ctx->dev; - struct s5p_mfc_buf *dst_buf, *src_buf; - u32 dec_y_addr; - unsigned int frame_type; - - /* Make sure we actually have a new frame before continuing. */ - frame_type = s5p_mfc_hw_call(dev->mfc_ops, get_dec_frame_type, dev); - if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) - return; - dec_y_addr = (u32)s5p_mfc_hw_call(dev->mfc_ops, get_dec_y_adr, dev); - - /* Copy timestamp / timecode from decoded src to dst and set - appropriate flags. */ - src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, list); - list_for_each_entry(dst_buf, &ctx->dst_queue, list) { - u32 addr = (u32)vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0); - - if (addr == dec_y_addr) { - dst_buf->b->timecode = src_buf->b->timecode; - dst_buf->b->vb2_buf.timestamp = - src_buf->b->vb2_buf.timestamp; - dst_buf->b->flags &= - ~V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - dst_buf->b->flags |= - src_buf->b->flags - & V4L2_BUF_FLAG_TSTAMP_SRC_MASK; - switch (frame_type) { - case S5P_FIMV_DECODE_FRAME_I_FRAME: - dst_buf->b->flags |= - V4L2_BUF_FLAG_KEYFRAME; - break; - case S5P_FIMV_DECODE_FRAME_P_FRAME: - dst_buf->b->flags |= - V4L2_BUF_FLAG_PFRAME; - break; - case S5P_FIMV_DECODE_FRAME_B_FRAME: - dst_buf->b->flags |= - V4L2_BUF_FLAG_BFRAME; - break; - default: - /* Don't know how to handle - S5P_FIMV_DECODE_FRAME_OTHER_FRAME. */ - mfc_debug(2, "Unexpected frame type: %d\n", - frame_type); - } - break; - } - } -} - -static void s5p_mfc_handle_frame_new(struct s5p_mfc_ctx *ctx, unsigned int err) -{ - struct s5p_mfc_dev *dev = ctx->dev; - struct s5p_mfc_buf *dst_buf; - u32 dspl_y_addr; - unsigned int frame_type; - - dspl_y_addr = (u32)s5p_mfc_hw_call(dev->mfc_ops, get_dspl_y_adr, dev); - if (IS_MFCV6_PLUS(dev)) - frame_type = s5p_mfc_hw_call(dev->mfc_ops, - get_disp_frame_type, ctx); - else - frame_type = s5p_mfc_hw_call(dev->mfc_ops, - get_dec_frame_type, dev); - - /* If frame is same as previous then skip and do not dequeue */ - if (frame_type == S5P_FIMV_DECODE_FRAME_SKIPPED) { - if (!ctx->after_packed_pb) - ctx->sequence++; - ctx->after_packed_pb = 0; - return; - } - ctx->sequence++; - /* The MFC returns address of the buffer, now we have to - * check which videobuf does it correspond to */ - list_for_each_entry(dst_buf, &ctx->dst_queue, list) { - u32 addr = (u32)vb2_dma_contig_plane_dma_addr(&dst_buf->b->vb2_buf, 0); - - /* Check if this is the buffer we're looking for */ - if (addr == dspl_y_addr) { - list_del(&dst_buf->list); - ctx->dst_queue_cnt--; - dst_buf->b->sequence = ctx->sequence; - if (s5p_mfc_hw_call(dev->mfc_ops, - get_pic_type_top, ctx) == - s5p_mfc_hw_call(dev->mfc_ops, - get_pic_type_bot, ctx)) - dst_buf->b->field = V4L2_FIELD_NONE; - else - dst_buf->b->field = - V4L2_FIELD_INTERLACED; - vb2_set_plane_payload(&dst_buf->b->vb2_buf, 0, - ctx->luma_size); - vb2_set_plane_payload(&dst_buf->b->vb2_buf, 1, - ctx->chroma_size); - clear_bit(dst_buf->b->vb2_buf.index, - &ctx->dec_dst_flag); - - vb2_buffer_done(&dst_buf->b->vb2_buf, err ? - VB2_BUF_STATE_ERROR : VB2_BUF_STATE_DONE); - - break; - } - } -} - -/* Handle frame decoding interrupt */ -static void s5p_mfc_handle_frame(struct s5p_mfc_ctx *ctx, - unsigned int reason, unsigned int err) -{ - struct s5p_mfc_dev *dev = ctx->dev; - unsigned int dst_frame_status; - unsigned int dec_frame_status; - struct s5p_mfc_buf *src_buf; - unsigned int res_change; - - dst_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev) - & S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK; - dec_frame_status = s5p_mfc_hw_call(dev->mfc_ops, get_dec_status, dev) - & S5P_FIMV_DEC_STATUS_DECODING_STATUS_MASK; - res_change = (s5p_mfc_hw_call(dev->mfc_ops, get_dspl_status, dev) - & S5P_FIMV_DEC_STATUS_RESOLUTION_MASK) - >> S5P_FIMV_DEC_STATUS_RESOLUTION_SHIFT; - mfc_debug(2, "Frame Status: %x\n", dst_frame_status); - if (ctx->state == MFCINST_RES_CHANGE_INIT) - ctx->state = MFCINST_RES_CHANGE_FLUSH; - if (res_change == S5P_FIMV_RES_INCREASE || - res_change == S5P_FIMV_RES_DECREASE) { - ctx->state = MFCINST_RES_CHANGE_INIT; - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); - wake_up_ctx(ctx, reason, err); - WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); - s5p_mfc_clock_off(); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); - return; - } - if (ctx->dpb_flush_flag) - ctx->dpb_flush_flag = 0; - - /* All frames remaining in the buffer have been extracted */ - if (dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_EMPTY) { - if (ctx->state == MFCINST_RES_CHANGE_FLUSH) { - static const struct v4l2_event ev_src_ch = { - .type = V4L2_EVENT_SOURCE_CHANGE, - .u.src_change.changes = - V4L2_EVENT_SRC_CH_RESOLUTION, - }; - - s5p_mfc_handle_frame_all_extracted(ctx); - ctx->state = MFCINST_RES_CHANGE_END; - v4l2_event_queue_fh(&ctx->fh, &ev_src_ch); - - goto leave_handle_frame; - } else { - s5p_mfc_handle_frame_all_extracted(ctx); - } - } - - if (dec_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) - s5p_mfc_handle_frame_copy_time(ctx); - - /* A frame has been decoded and is in the buffer */ - if (dst_frame_status == S5P_FIMV_DEC_STATUS_DISPLAY_ONLY || - dst_frame_status == S5P_FIMV_DEC_STATUS_DECODING_DISPLAY) { - s5p_mfc_handle_frame_new(ctx, err); - } else { - mfc_debug(2, "No frame decode\n"); - } - /* Mark source buffer as complete */ - if (dst_frame_status != S5P_FIMV_DEC_STATUS_DISPLAY_ONLY - && !list_empty(&ctx->src_queue)) { - src_buf = list_entry(ctx->src_queue.next, struct s5p_mfc_buf, - list); - ctx->consumed_stream += s5p_mfc_hw_call(dev->mfc_ops, - get_consumed_stream, dev); - if (ctx->codec_mode != S5P_MFC_CODEC_H264_DEC && - ctx->codec_mode != S5P_MFC_CODEC_VP8_DEC && - ctx->consumed_stream + STUFF_BYTE < - src_buf->b->vb2_buf.planes[0].bytesused) { - /* Run MFC again on the same buffer */ - mfc_debug(2, "Running again the same buffer\n"); - ctx->after_packed_pb = 1; - } else { - mfc_debug(2, "MFC needs next buffer\n"); - ctx->consumed_stream = 0; - if (src_buf->flags & MFC_BUF_FLAG_EOS) - ctx->state = MFCINST_FINISHING; - list_del(&src_buf->list); - ctx->src_queue_cnt--; - if (s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) > 0) - vb2_buffer_done(&src_buf->b->vb2_buf, - VB2_BUF_STATE_ERROR); - else - vb2_buffer_done(&src_buf->b->vb2_buf, - VB2_BUF_STATE_DONE); - } - } -leave_handle_frame: - if ((ctx->src_queue_cnt == 0 && ctx->state != MFCINST_FINISHING) - || ctx->dst_queue_cnt < ctx->pb_count) - clear_work_bit(ctx); - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); - wake_up_ctx(ctx, reason, err); - WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); - s5p_mfc_clock_off(); - /* if suspending, wake up device and do not try_run again*/ - if (test_bit(0, &dev->enter_suspend)) - wake_up_dev(dev, reason, err); - else - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); -} - -/* Error handling for interrupt */ -static void s5p_mfc_handle_error(struct s5p_mfc_dev *dev, - struct s5p_mfc_ctx *ctx, unsigned int reason, unsigned int err) -{ - mfc_err("Interrupt Error: %08x\n", err); - - if (ctx) { - /* Error recovery is dependent on the state of context */ - switch (ctx->state) { - case MFCINST_RES_CHANGE_INIT: - case MFCINST_RES_CHANGE_FLUSH: - case MFCINST_RES_CHANGE_END: - case MFCINST_FINISHING: - case MFCINST_FINISHED: - case MFCINST_RUNNING: - /* It is highly probable that an error occurred - * while decoding a frame */ - clear_work_bit(ctx); - ctx->state = MFCINST_ERROR; - /* Mark all dst buffers as having an error */ - s5p_mfc_cleanup_queue(&ctx->dst_queue, &ctx->vq_dst); - /* Mark all src buffers as having an error */ - s5p_mfc_cleanup_queue(&ctx->src_queue, &ctx->vq_src); - wake_up_ctx(ctx, reason, err); - break; - default: - clear_work_bit(ctx); - ctx->state = MFCINST_ERROR; - wake_up_ctx(ctx, reason, err); - break; - } - } - WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); - s5p_mfc_clock_off(); - wake_up_dev(dev, reason, err); -} - -/* Header parsing interrupt handling */ -static void s5p_mfc_handle_seq_done(struct s5p_mfc_ctx *ctx, - unsigned int reason, unsigned int err) -{ - struct s5p_mfc_dev *dev; - - if (!ctx) - return; - dev = ctx->dev; - if (ctx->c_ops->post_seq_start) { - if (ctx->c_ops->post_seq_start(ctx)) - mfc_err("post_seq_start() failed\n"); - } else { - ctx->img_width = s5p_mfc_hw_call(dev->mfc_ops, get_img_width, - dev); - ctx->img_height = s5p_mfc_hw_call(dev->mfc_ops, get_img_height, - dev); - - s5p_mfc_hw_call(dev->mfc_ops, dec_calc_dpb_size, ctx); - - ctx->pb_count = s5p_mfc_hw_call(dev->mfc_ops, get_dpb_count, - dev); - ctx->mv_count = s5p_mfc_hw_call(dev->mfc_ops, get_mv_count, - dev); - if (FW_HAS_E_MIN_SCRATCH_BUF(dev)) - ctx->scratch_buf_size = s5p_mfc_hw_call(dev->mfc_ops, - get_min_scratch_buf_size, dev); - if (ctx->img_width == 0 || ctx->img_height == 0) - ctx->state = MFCINST_ERROR; - else - ctx->state = MFCINST_HEAD_PARSED; - - if ((ctx->codec_mode == S5P_MFC_CODEC_H264_DEC || - ctx->codec_mode == S5P_MFC_CODEC_H264_MVC_DEC) && - !list_empty(&ctx->src_queue)) { - struct s5p_mfc_buf *src_buf; - src_buf = list_entry(ctx->src_queue.next, - struct s5p_mfc_buf, list); - if (s5p_mfc_hw_call(dev->mfc_ops, get_consumed_stream, - dev) < - src_buf->b->vb2_buf.planes[0].bytesused) - ctx->head_processed = 0; - else - ctx->head_processed = 1; - } else { - ctx->head_processed = 1; - } - } - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); - clear_work_bit(ctx); - WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); - s5p_mfc_clock_off(); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); - wake_up_ctx(ctx, reason, err); -} - -/* Header parsing interrupt handling */ -static void s5p_mfc_handle_init_buffers(struct s5p_mfc_ctx *ctx, - unsigned int reason, unsigned int err) -{ - struct s5p_mfc_buf *src_buf; - struct s5p_mfc_dev *dev; - - if (!ctx) - return; - dev = ctx->dev; - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); - ctx->int_type = reason; - ctx->int_err = err; - ctx->int_cond = 1; - clear_work_bit(ctx); - if (err == 0) { - ctx->state = MFCINST_RUNNING; - if (!ctx->dpb_flush_flag && ctx->head_processed) { - if (!list_empty(&ctx->src_queue)) { - src_buf = list_entry(ctx->src_queue.next, - struct s5p_mfc_buf, list); - list_del(&src_buf->list); - ctx->src_queue_cnt--; - vb2_buffer_done(&src_buf->b->vb2_buf, - VB2_BUF_STATE_DONE); - } - } else { - ctx->dpb_flush_flag = 0; - } - WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); - - s5p_mfc_clock_off(); - - wake_up(&ctx->queue); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); - } else { - WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); - - s5p_mfc_clock_off(); - - wake_up(&ctx->queue); - } -} - -static void s5p_mfc_handle_stream_complete(struct s5p_mfc_ctx *ctx) -{ - struct s5p_mfc_dev *dev = ctx->dev; - struct s5p_mfc_buf *mb_entry; - - mfc_debug(2, "Stream completed\n"); - - ctx->state = MFCINST_FINISHED; - - if (!list_empty(&ctx->dst_queue)) { - mb_entry = list_entry(ctx->dst_queue.next, struct s5p_mfc_buf, - list); - list_del(&mb_entry->list); - ctx->dst_queue_cnt--; - vb2_set_plane_payload(&mb_entry->b->vb2_buf, 0, 0); - vb2_buffer_done(&mb_entry->b->vb2_buf, VB2_BUF_STATE_DONE); - } - - clear_work_bit(ctx); - - WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); - - s5p_mfc_clock_off(); - wake_up(&ctx->queue); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); -} - -/* Interrupt processing */ -static irqreturn_t s5p_mfc_irq(int irq, void *priv) -{ - struct s5p_mfc_dev *dev = priv; - struct s5p_mfc_ctx *ctx; - unsigned int reason; - unsigned int err; - - mfc_debug_enter(); - /* Reset the timeout watchdog */ - atomic_set(&dev->watchdog_cnt, 0); - spin_lock(&dev->irqlock); - ctx = dev->ctx[dev->curr_ctx]; - /* Get the reason of interrupt and the error code */ - reason = s5p_mfc_hw_call(dev->mfc_ops, get_int_reason, dev); - err = s5p_mfc_hw_call(dev->mfc_ops, get_int_err, dev); - mfc_debug(1, "Int reason: %d (err: %08x)\n", reason, err); - switch (reason) { - case S5P_MFC_R2H_CMD_ERR_RET: - /* An error has occurred */ - if (ctx->state == MFCINST_RUNNING && - (s5p_mfc_hw_call(dev->mfc_ops, err_dec, err) >= - dev->warn_start || - err == S5P_FIMV_ERR_NO_VALID_SEQ_HDR || - err == S5P_FIMV_ERR_INCOMPLETE_FRAME || - err == S5P_FIMV_ERR_TIMEOUT)) - s5p_mfc_handle_frame(ctx, reason, err); - else - s5p_mfc_handle_error(dev, ctx, reason, err); - clear_bit(0, &dev->enter_suspend); - break; - - case S5P_MFC_R2H_CMD_SLICE_DONE_RET: - case S5P_MFC_R2H_CMD_FIELD_DONE_RET: - case S5P_MFC_R2H_CMD_FRAME_DONE_RET: - if (ctx->c_ops->post_frame_start) { - if (ctx->c_ops->post_frame_start(ctx)) - mfc_err("post_frame_start() failed\n"); - - if (ctx->state == MFCINST_FINISHING && - list_empty(&ctx->ref_queue)) { - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); - s5p_mfc_handle_stream_complete(ctx); - break; - } - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); - WARN_ON(test_and_clear_bit(0, &dev->hw_lock) == 0); - s5p_mfc_clock_off(); - wake_up_ctx(ctx, reason, err); - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); - } else { - s5p_mfc_handle_frame(ctx, reason, err); - } - break; - - case S5P_MFC_R2H_CMD_SEQ_DONE_RET: - s5p_mfc_handle_seq_done(ctx, reason, err); - break; - - case S5P_MFC_R2H_CMD_OPEN_INSTANCE_RET: - ctx->inst_no = s5p_mfc_hw_call(dev->mfc_ops, get_inst_no, dev); - ctx->state = MFCINST_GOT_INST; - goto irq_cleanup_hw; - - case S5P_MFC_R2H_CMD_CLOSE_INSTANCE_RET: - ctx->inst_no = MFC_NO_INSTANCE_SET; - ctx->state = MFCINST_FREE; - goto irq_cleanup_hw; - - case S5P_MFC_R2H_CMD_SYS_INIT_RET: - case S5P_MFC_R2H_CMD_FW_STATUS_RET: - case S5P_MFC_R2H_CMD_SLEEP_RET: - case S5P_MFC_R2H_CMD_WAKEUP_RET: - if (ctx) - clear_work_bit(ctx); - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); - clear_bit(0, &dev->hw_lock); - clear_bit(0, &dev->enter_suspend); - wake_up_dev(dev, reason, err); - break; - - case S5P_MFC_R2H_CMD_INIT_BUFFERS_RET: - s5p_mfc_handle_init_buffers(ctx, reason, err); - break; - - case S5P_MFC_R2H_CMD_COMPLETE_SEQ_RET: - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); - ctx->int_type = reason; - ctx->int_err = err; - s5p_mfc_handle_stream_complete(ctx); - break; - - case S5P_MFC_R2H_CMD_DPB_FLUSH_RET: - ctx->state = MFCINST_RUNNING; - goto irq_cleanup_hw; - - default: - mfc_debug(2, "Unknown int reason\n"); - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); - } - spin_unlock(&dev->irqlock); - mfc_debug_leave(); - return IRQ_HANDLED; -irq_cleanup_hw: - s5p_mfc_hw_call(dev->mfc_ops, clear_int_flags, dev); - ctx->int_type = reason; - ctx->int_err = err; - ctx->int_cond = 1; - if (test_and_clear_bit(0, &dev->hw_lock) == 0) - mfc_err("Failed to unlock hw\n"); - - s5p_mfc_clock_off(); - clear_work_bit(ctx); - wake_up(&ctx->queue); - - s5p_mfc_hw_call(dev->mfc_ops, try_run, dev); - spin_unlock(&dev->irqlock); - mfc_debug(2, "Exit via irq_cleanup_hw\n"); - return IRQ_HANDLED; -} - -/* Open an MFC node */ -static int s5p_mfc_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct s5p_mfc_dev *dev = video_drvdata(file); - struct s5p_mfc_ctx *ctx = NULL; - struct vb2_queue *q; - int ret = 0; - - mfc_debug_enter(); - if (mutex_lock_interruptible(&dev->mfc_mutex)) - return -ERESTARTSYS; - dev->num_inst++; /* It is guarded by mfc_mutex in vfd */ - /* Allocate memory for context */ - ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); - if (!ctx) { - ret = -ENOMEM; - goto err_alloc; - } - init_waitqueue_head(&ctx->queue); - v4l2_fh_init(&ctx->fh, vdev); - file->private_data = &ctx->fh; - v4l2_fh_add(&ctx->fh); - ctx->dev = dev; - INIT_LIST_HEAD(&ctx->src_queue); - INIT_LIST_HEAD(&ctx->dst_queue); - ctx->src_queue_cnt = 0; - ctx->dst_queue_cnt = 0; - /* Get context number */ - ctx->num = 0; - while (dev->ctx[ctx->num]) { - ctx->num++; - if (ctx->num >= MFC_NUM_CONTEXTS) { - mfc_debug(2, "Too many open contexts\n"); - ret = -EBUSY; - goto err_no_ctx; - } - } - /* Mark context as idle */ - clear_work_bit_irqsave(ctx); - dev->ctx[ctx->num] = ctx; - if (vdev == dev->vfd_dec) { - ctx->type = MFCINST_DECODER; - ctx->c_ops = get_dec_codec_ops(); - s5p_mfc_dec_init(ctx); - /* Setup ctrl handler */ - ret = s5p_mfc_dec_ctrls_setup(ctx); - if (ret) { - mfc_err("Failed to setup mfc controls\n"); - goto err_ctrls_setup; - } - } else if (vdev == dev->vfd_enc) { - ctx->type = MFCINST_ENCODER; - ctx->c_ops = get_enc_codec_ops(); - /* only for encoder */ - INIT_LIST_HEAD(&ctx->ref_queue); - ctx->ref_queue_cnt = 0; - s5p_mfc_enc_init(ctx); - /* Setup ctrl handler */ - ret = s5p_mfc_enc_ctrls_setup(ctx); - if (ret) { - mfc_err("Failed to setup mfc controls\n"); - goto err_ctrls_setup; - } - } else { - ret = -ENOENT; - goto err_bad_node; - } - ctx->fh.ctrl_handler = &ctx->ctrl_handler; - ctx->inst_no = MFC_NO_INSTANCE_SET; - /* Load firmware if this is the first instance */ - if (dev->num_inst == 1) { - dev->watchdog_timer.expires = jiffies + - msecs_to_jiffies(MFC_WATCHDOG_INTERVAL); - add_timer(&dev->watchdog_timer); - ret = s5p_mfc_power_on(); - if (ret < 0) { - mfc_err("power on failed\n"); - goto err_pwr_enable; - } - s5p_mfc_clock_on(); - ret = s5p_mfc_load_firmware(dev); - if (ret) { - s5p_mfc_clock_off(); - goto err_load_fw; - } - /* Init the FW */ - ret = s5p_mfc_init_hw(dev); - s5p_mfc_clock_off(); - if (ret) - goto err_init_hw; - } - /* Init videobuf2 queue for CAPTURE */ - q = &ctx->vq_dst; - q->type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE; - q->drv_priv = &ctx->fh; - q->lock = &dev->mfc_mutex; - if (vdev == dev->vfd_dec) { - q->io_modes = VB2_MMAP; - q->ops = get_dec_queue_ops(); - } else if (vdev == dev->vfd_enc) { - q->io_modes = VB2_MMAP | VB2_USERPTR; - q->ops = get_enc_queue_ops(); - } else { - ret = -ENOENT; - goto err_queue_init; - } - /* - * We'll do mostly sequential access, so sacrifice TLB efficiency for - * faster allocation. - */ - q->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES; - q->mem_ops = &vb2_dma_contig_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - ret = vb2_queue_init(q); - if (ret) { - mfc_err("Failed to initialize videobuf2 queue(capture)\n"); - goto err_queue_init; - } - /* Init videobuf2 queue for OUTPUT */ - q = &ctx->vq_src; - q->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE; - q->drv_priv = &ctx->fh; - q->lock = &dev->mfc_mutex; - if (vdev == dev->vfd_dec) { - q->io_modes = VB2_MMAP; - q->ops = get_dec_queue_ops(); - } else if (vdev == dev->vfd_enc) { - q->io_modes = VB2_MMAP | VB2_USERPTR; - q->ops = get_enc_queue_ops(); - } else { - ret = -ENOENT; - goto err_queue_init; - } - /* One way to indicate end-of-stream for MFC is to set the - * bytesused == 0. However by default videobuf2 handles bytesused - * equal to 0 as a special case and changes its value to the size - * of the buffer. Set the allow_zero_bytesused flag so that videobuf2 - * will keep the value of bytesused intact. - */ - q->allow_zero_bytesused = 1; - - /* - * We'll do mostly sequential access, so sacrifice TLB efficiency for - * faster allocation. - */ - q->dma_attrs = DMA_ATTR_ALLOC_SINGLE_PAGES; - q->mem_ops = &vb2_dma_contig_memops; - q->timestamp_flags = V4L2_BUF_FLAG_TIMESTAMP_COPY; - ret = vb2_queue_init(q); - if (ret) { - mfc_err("Failed to initialize videobuf2 queue(output)\n"); - goto err_queue_init; - } - mutex_unlock(&dev->mfc_mutex); - mfc_debug_leave(); - return ret; - /* Deinit when failure occurred */ -err_queue_init: - if (dev->num_inst == 1) - s5p_mfc_deinit_hw(dev); -err_init_hw: -err_load_fw: -err_pwr_enable: - if (dev->num_inst == 1) { - if (s5p_mfc_power_off() < 0) - mfc_err("power off failed\n"); - del_timer_sync(&dev->watchdog_timer); - } -err_ctrls_setup: - s5p_mfc_dec_ctrls_delete(ctx); -err_bad_node: - dev->ctx[ctx->num] = NULL; -err_no_ctx: - v4l2_fh_del(&ctx->fh); - v4l2_fh_exit(&ctx->fh); - kfree(ctx); -err_alloc: - dev->num_inst--; - mutex_unlock(&dev->mfc_mutex); - mfc_debug_leave(); - return ret; -} - -/* Release MFC context */ -static int s5p_mfc_release(struct file *file) -{ - struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data); - struct s5p_mfc_dev *dev = ctx->dev; - - /* if dev is null, do cleanup that doesn't need dev */ - mfc_debug_enter(); - if (dev) - mutex_lock(&dev->mfc_mutex); - vb2_queue_release(&ctx->vq_src); - vb2_queue_release(&ctx->vq_dst); - if (dev) { - s5p_mfc_clock_on(); - - /* Mark context as idle */ - clear_work_bit_irqsave(ctx); - /* - * If instance was initialised and not yet freed, - * return instance and free resources - */ - if (ctx->state != MFCINST_FREE && ctx->state != MFCINST_INIT) { - mfc_debug(2, "Has to free instance\n"); - s5p_mfc_close_mfc_inst(dev, ctx); - } - /* hardware locking scheme */ - if (dev->curr_ctx == ctx->num) - clear_bit(0, &dev->hw_lock); - dev->num_inst--; - if (dev->num_inst == 0) { - mfc_debug(2, "Last instance\n"); - s5p_mfc_deinit_hw(dev); - del_timer_sync(&dev->watchdog_timer); - s5p_mfc_clock_off(); - if (s5p_mfc_power_off() < 0) - mfc_err("Power off failed\n"); - } else { - mfc_debug(2, "Shutting down clock\n"); - s5p_mfc_clock_off(); - } - } - if (dev) - dev->ctx[ctx->num] = NULL; - s5p_mfc_dec_ctrls_delete(ctx); - v4l2_fh_del(&ctx->fh); - /* vdev is gone if dev is null */ - if (dev) - v4l2_fh_exit(&ctx->fh); - kfree(ctx); - mfc_debug_leave(); - if (dev) - mutex_unlock(&dev->mfc_mutex); - - return 0; -} - -/* Poll */ -static __poll_t s5p_mfc_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data); - struct s5p_mfc_dev *dev = ctx->dev; - struct vb2_queue *src_q, *dst_q; - struct vb2_buffer *src_vb = NULL, *dst_vb = NULL; - __poll_t rc = 0; - unsigned long flags; - - mutex_lock(&dev->mfc_mutex); - src_q = &ctx->vq_src; - dst_q = &ctx->vq_dst; - /* - * There has to be at least one buffer queued on each queued_list, which - * means either in driver already or waiting for driver to claim it - * and start processing. - */ - if ((!src_q->streaming || list_empty(&src_q->queued_list)) - && (!dst_q->streaming || list_empty(&dst_q->queued_list))) { - rc = EPOLLERR; - goto end; - } - mutex_unlock(&dev->mfc_mutex); - poll_wait(file, &ctx->fh.wait, wait); - poll_wait(file, &src_q->done_wq, wait); - poll_wait(file, &dst_q->done_wq, wait); - mutex_lock(&dev->mfc_mutex); - if (v4l2_event_pending(&ctx->fh)) - rc |= EPOLLPRI; - spin_lock_irqsave(&src_q->done_lock, flags); - if (!list_empty(&src_q->done_list)) - src_vb = list_first_entry(&src_q->done_list, struct vb2_buffer, - done_entry); - if (src_vb && (src_vb->state == VB2_BUF_STATE_DONE - || src_vb->state == VB2_BUF_STATE_ERROR)) - rc |= EPOLLOUT | EPOLLWRNORM; - spin_unlock_irqrestore(&src_q->done_lock, flags); - spin_lock_irqsave(&dst_q->done_lock, flags); - if (!list_empty(&dst_q->done_list)) - dst_vb = list_first_entry(&dst_q->done_list, struct vb2_buffer, - done_entry); - if (dst_vb && (dst_vb->state == VB2_BUF_STATE_DONE - || dst_vb->state == VB2_BUF_STATE_ERROR)) - rc |= EPOLLIN | EPOLLRDNORM; - spin_unlock_irqrestore(&dst_q->done_lock, flags); -end: - mutex_unlock(&dev->mfc_mutex); - return rc; -} - -/* Mmap */ -static int s5p_mfc_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct s5p_mfc_ctx *ctx = fh_to_ctx(file->private_data); - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - int ret; - - if (offset < DST_QUEUE_OFF_BASE) { - mfc_debug(2, "mmaping source\n"); - ret = vb2_mmap(&ctx->vq_src, vma); - } else { /* capture */ - mfc_debug(2, "mmaping destination\n"); - vma->vm_pgoff -= (DST_QUEUE_OFF_BASE >> PAGE_SHIFT); - ret = vb2_mmap(&ctx->vq_dst, vma); - } - return ret; -} - -/* v4l2 ops */ -static const struct v4l2_file_operations s5p_mfc_fops = { - .owner = THIS_MODULE, - .open = s5p_mfc_open, - .release = s5p_mfc_release, - .poll = s5p_mfc_poll, - .unlocked_ioctl = video_ioctl2, - .mmap = s5p_mfc_mmap, -}; - -/* DMA memory related helper functions */ -static void s5p_mfc_memdev_release(struct device *dev) -{ - of_reserved_mem_device_release(dev); -} - -static struct device *s5p_mfc_alloc_memdev(struct device *dev, - const char *name, unsigned int idx) -{ - struct device *child; - int ret; - - child = devm_kzalloc(dev, sizeof(*child), GFP_KERNEL); - if (!child) - return NULL; - - device_initialize(child); - dev_set_name(child, "%s:%s", dev_name(dev), name); - child->parent = dev; - child->coherent_dma_mask = dev->coherent_dma_mask; - child->dma_mask = dev->dma_mask; - child->release = s5p_mfc_memdev_release; - child->dma_parms = devm_kzalloc(dev, sizeof(*child->dma_parms), - GFP_KERNEL); - if (!child->dma_parms) - goto err; - - /* - * The memdevs are not proper OF platform devices, so in order for them - * to be treated as valid DMA masters we need a bit of a hack to force - * them to inherit the MFC node's DMA configuration. - */ - of_dma_configure(child, dev->of_node, true); - - if (device_add(child) == 0) { - ret = of_reserved_mem_device_init_by_idx(child, dev->of_node, - idx); - if (ret == 0) - return child; - device_del(child); - } -err: - put_device(child); - return NULL; -} - -static int s5p_mfc_configure_2port_memory(struct s5p_mfc_dev *mfc_dev) -{ - struct device *dev = &mfc_dev->plat_dev->dev; - void *bank2_virt; - dma_addr_t bank2_dma_addr; - unsigned long align_size = 1 << MFC_BASE_ALIGN_ORDER; - int ret; - - /* - * Create and initialize virtual devices for accessing - * reserved memory regions. - */ - mfc_dev->mem_dev[BANK_L_CTX] = s5p_mfc_alloc_memdev(dev, "left", - BANK_L_CTX); - if (!mfc_dev->mem_dev[BANK_L_CTX]) - return -ENODEV; - mfc_dev->mem_dev[BANK_R_CTX] = s5p_mfc_alloc_memdev(dev, "right", - BANK_R_CTX); - if (!mfc_dev->mem_dev[BANK_R_CTX]) { - device_unregister(mfc_dev->mem_dev[BANK_L_CTX]); - return -ENODEV; - } - - /* Allocate memory for firmware and initialize both banks addresses */ - ret = s5p_mfc_alloc_firmware(mfc_dev); - if (ret) { - device_unregister(mfc_dev->mem_dev[BANK_R_CTX]); - device_unregister(mfc_dev->mem_dev[BANK_L_CTX]); - return ret; - } - - mfc_dev->dma_base[BANK_L_CTX] = mfc_dev->fw_buf.dma; - - bank2_virt = dma_alloc_coherent(mfc_dev->mem_dev[BANK_R_CTX], - align_size, &bank2_dma_addr, GFP_KERNEL); - if (!bank2_virt) { - mfc_err("Allocating bank2 base failed\n"); - s5p_mfc_release_firmware(mfc_dev); - device_unregister(mfc_dev->mem_dev[BANK_R_CTX]); - device_unregister(mfc_dev->mem_dev[BANK_L_CTX]); - return -ENOMEM; - } - - /* Valid buffers passed to MFC encoder with LAST_FRAME command - * should not have address of bank2 - MFC will treat it as a null frame. - * To avoid such situation we set bank2 address below the pool address. - */ - mfc_dev->dma_base[BANK_R_CTX] = bank2_dma_addr - align_size; - - dma_free_coherent(mfc_dev->mem_dev[BANK_R_CTX], align_size, bank2_virt, - bank2_dma_addr); - - vb2_dma_contig_set_max_seg_size(mfc_dev->mem_dev[BANK_L_CTX], - DMA_BIT_MASK(32)); - vb2_dma_contig_set_max_seg_size(mfc_dev->mem_dev[BANK_R_CTX], - DMA_BIT_MASK(32)); - - return 0; -} - -static void s5p_mfc_unconfigure_2port_memory(struct s5p_mfc_dev *mfc_dev) -{ - device_unregister(mfc_dev->mem_dev[BANK_L_CTX]); - device_unregister(mfc_dev->mem_dev[BANK_R_CTX]); - vb2_dma_contig_clear_max_seg_size(mfc_dev->mem_dev[BANK_L_CTX]); - vb2_dma_contig_clear_max_seg_size(mfc_dev->mem_dev[BANK_R_CTX]); -} - -static int s5p_mfc_configure_common_memory(struct s5p_mfc_dev *mfc_dev) -{ - struct device *dev = &mfc_dev->plat_dev->dev; - unsigned long mem_size = SZ_4M; - - if (IS_ENABLED(CONFIG_DMA_CMA) || exynos_is_iommu_available(dev)) - mem_size = SZ_8M; - - if (mfc_mem_size) - mem_size = memparse(mfc_mem_size, NULL); - - mfc_dev->mem_bitmap = bitmap_zalloc(mem_size >> PAGE_SHIFT, GFP_KERNEL); - if (!mfc_dev->mem_bitmap) - return -ENOMEM; - - mfc_dev->mem_virt = dma_alloc_coherent(dev, mem_size, - &mfc_dev->mem_base, GFP_KERNEL); - if (!mfc_dev->mem_virt) { - bitmap_free(mfc_dev->mem_bitmap); - dev_err(dev, "failed to preallocate %ld MiB for the firmware and context buffers\n", - (mem_size / SZ_1M)); - return -ENOMEM; - } - mfc_dev->mem_size = mem_size; - mfc_dev->dma_base[BANK_L_CTX] = mfc_dev->mem_base; - mfc_dev->dma_base[BANK_R_CTX] = mfc_dev->mem_base; - - /* - * MFC hardware cannot handle 0 as a base address, so mark first 128K - * as used (to keep required base alignment) and adjust base address - */ - if (mfc_dev->mem_base == (dma_addr_t)0) { - unsigned int offset = 1 << MFC_BASE_ALIGN_ORDER; - - bitmap_set(mfc_dev->mem_bitmap, 0, offset >> PAGE_SHIFT); - mfc_dev->dma_base[BANK_L_CTX] += offset; - mfc_dev->dma_base[BANK_R_CTX] += offset; - } - - /* Firmware allocation cannot fail in this case */ - s5p_mfc_alloc_firmware(mfc_dev); - - mfc_dev->mem_dev[BANK_L_CTX] = mfc_dev->mem_dev[BANK_R_CTX] = dev; - vb2_dma_contig_set_max_seg_size(dev, DMA_BIT_MASK(32)); - - dev_info(dev, "preallocated %ld MiB buffer for the firmware and context buffers\n", - (mem_size / SZ_1M)); - - return 0; -} - -static void s5p_mfc_unconfigure_common_memory(struct s5p_mfc_dev *mfc_dev) -{ - struct device *dev = &mfc_dev->plat_dev->dev; - - dma_free_coherent(dev, mfc_dev->mem_size, mfc_dev->mem_virt, - mfc_dev->mem_base); - bitmap_free(mfc_dev->mem_bitmap); - vb2_dma_contig_clear_max_seg_size(dev); -} - -static int s5p_mfc_configure_dma_memory(struct s5p_mfc_dev *mfc_dev) -{ - struct device *dev = &mfc_dev->plat_dev->dev; - - if (exynos_is_iommu_available(dev) || !IS_TWOPORT(mfc_dev)) - return s5p_mfc_configure_common_memory(mfc_dev); - else - return s5p_mfc_configure_2port_memory(mfc_dev); -} - -static void s5p_mfc_unconfigure_dma_memory(struct s5p_mfc_dev *mfc_dev) -{ - struct device *dev = &mfc_dev->plat_dev->dev; - - s5p_mfc_release_firmware(mfc_dev); - if (exynos_is_iommu_available(dev) || !IS_TWOPORT(mfc_dev)) - s5p_mfc_unconfigure_common_memory(mfc_dev); - else - s5p_mfc_unconfigure_2port_memory(mfc_dev); -} - -/* MFC probe function */ -static int s5p_mfc_probe(struct platform_device *pdev) -{ - struct s5p_mfc_dev *dev; - struct video_device *vfd; - int ret; - - pr_debug("%s++\n", __func__); - dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL); - if (!dev) - return -ENOMEM; - - spin_lock_init(&dev->irqlock); - spin_lock_init(&dev->condlock); - dev->plat_dev = pdev; - if (!dev->plat_dev) { - mfc_err("No platform data specified\n"); - return -ENODEV; - } - - dev->variant = of_device_get_match_data(&pdev->dev); - if (!dev->variant) { - dev_err(&pdev->dev, "Failed to get device MFC hardware variant information\n"); - return -ENOENT; - } - - dev->regs_base = devm_platform_ioremap_resource(pdev, 0); - if (IS_ERR(dev->regs_base)) - return PTR_ERR(dev->regs_base); - - ret = platform_get_irq(pdev, 0); - if (ret < 0) - return ret; - dev->irq = ret; - ret = devm_request_irq(&pdev->dev, dev->irq, s5p_mfc_irq, - 0, pdev->name, dev); - if (ret) { - dev_err(&pdev->dev, "Failed to install irq (%d)\n", ret); - return ret; - } - - ret = s5p_mfc_configure_dma_memory(dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to configure DMA memory\n"); - return ret; - } - - ret = s5p_mfc_init_pm(dev); - if (ret < 0) { - dev_err(&pdev->dev, "failed to get mfc clock source\n"); - goto err_dma; - } - - /* - * Load fails if fs isn't mounted. Try loading anyway. - * _open() will load it, it it fails now. Ignore failure. - */ - s5p_mfc_load_firmware(dev); - - mutex_init(&dev->mfc_mutex); - init_waitqueue_head(&dev->queue); - dev->hw_lock = 0; - INIT_WORK(&dev->watchdog_work, s5p_mfc_watchdog_worker); - atomic_set(&dev->watchdog_cnt, 0); - timer_setup(&dev->watchdog_timer, s5p_mfc_watchdog, 0); - - ret = v4l2_device_register(&pdev->dev, &dev->v4l2_dev); - if (ret) - goto err_v4l2_dev_reg; - - /* decoder */ - vfd = video_device_alloc(); - if (!vfd) { - v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); - ret = -ENOMEM; - goto err_dec_alloc; - } - vfd->fops = &s5p_mfc_fops; - vfd->ioctl_ops = get_dec_v4l2_ioctl_ops(); - vfd->release = video_device_release; - vfd->lock = &dev->mfc_mutex; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->vfl_dir = VFL_DIR_M2M; - vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - set_bit(V4L2_FL_QUIRK_INVERTED_CROP, &vfd->flags); - snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_DEC_NAME); - dev->vfd_dec = vfd; - video_set_drvdata(vfd, dev); - - /* encoder */ - vfd = video_device_alloc(); - if (!vfd) { - v4l2_err(&dev->v4l2_dev, "Failed to allocate video device\n"); - ret = -ENOMEM; - goto err_enc_alloc; - } - vfd->fops = &s5p_mfc_fops; - vfd->ioctl_ops = get_enc_v4l2_ioctl_ops(); - vfd->release = video_device_release; - vfd->lock = &dev->mfc_mutex; - vfd->v4l2_dev = &dev->v4l2_dev; - vfd->vfl_dir = VFL_DIR_M2M; - vfd->device_caps = V4L2_CAP_VIDEO_M2M_MPLANE | V4L2_CAP_STREAMING; - snprintf(vfd->name, sizeof(vfd->name), "%s", S5P_MFC_ENC_NAME); - dev->vfd_enc = vfd; - video_set_drvdata(vfd, dev); - platform_set_drvdata(pdev, dev); - - /* Initialize HW ops and commands based on MFC version */ - s5p_mfc_init_hw_ops(dev); - s5p_mfc_init_hw_cmds(dev); - s5p_mfc_init_regs(dev); - - /* Register decoder and encoder */ - ret = video_register_device(dev->vfd_dec, VFL_TYPE_VIDEO, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - goto err_dec_reg; - } - v4l2_info(&dev->v4l2_dev, - "decoder registered as /dev/video%d\n", dev->vfd_dec->num); - - ret = video_register_device(dev->vfd_enc, VFL_TYPE_VIDEO, 0); - if (ret) { - v4l2_err(&dev->v4l2_dev, "Failed to register video device\n"); - goto err_enc_reg; - } - v4l2_info(&dev->v4l2_dev, - "encoder registered as /dev/video%d\n", dev->vfd_enc->num); - - pr_debug("%s--\n", __func__); - return 0; - -/* Deinit MFC if probe had failed */ -err_enc_reg: - video_unregister_device(dev->vfd_dec); -err_dec_reg: - video_device_release(dev->vfd_enc); -err_enc_alloc: - video_device_release(dev->vfd_dec); -err_dec_alloc: - v4l2_device_unregister(&dev->v4l2_dev); -err_v4l2_dev_reg: - s5p_mfc_final_pm(dev); -err_dma: - s5p_mfc_unconfigure_dma_memory(dev); - - pr_debug("%s-- with error\n", __func__); - return ret; - -} - -/* Remove the driver */ -static int s5p_mfc_remove(struct platform_device *pdev) -{ - struct s5p_mfc_dev *dev = platform_get_drvdata(pdev); - struct s5p_mfc_ctx *ctx; - int i; - - v4l2_info(&dev->v4l2_dev, "Removing %s\n", pdev->name); - - /* - * Clear ctx dev pointer to avoid races between s5p_mfc_remove() - * and s5p_mfc_release() and s5p_mfc_release() accessing ctx->dev - * after s5p_mfc_remove() is run during unbind. - */ - mutex_lock(&dev->mfc_mutex); - for (i = 0; i < MFC_NUM_CONTEXTS; i++) { - ctx = dev->ctx[i]; - if (!ctx) - continue; - /* clear ctx->dev */ - ctx->dev = NULL; - } - mutex_unlock(&dev->mfc_mutex); - - del_timer_sync(&dev->watchdog_timer); - flush_work(&dev->watchdog_work); - - video_unregister_device(dev->vfd_enc); - video_unregister_device(dev->vfd_dec); - video_device_release(dev->vfd_enc); - video_device_release(dev->vfd_dec); - v4l2_device_unregister(&dev->v4l2_dev); - s5p_mfc_unconfigure_dma_memory(dev); - - s5p_mfc_final_pm(dev); - return 0; -} - -#ifdef CONFIG_PM_SLEEP - -static int s5p_mfc_suspend(struct device *dev) -{ - struct s5p_mfc_dev *m_dev = dev_get_drvdata(dev); - int ret; - - if (m_dev->num_inst == 0) - return 0; - - if (test_and_set_bit(0, &m_dev->enter_suspend) != 0) { - mfc_err("Error: going to suspend for a second time\n"); - return -EIO; - } - - /* Check if we're processing then wait if it necessary. */ - while (test_and_set_bit(0, &m_dev->hw_lock) != 0) { - /* Try and lock the HW */ - /* Wait on the interrupt waitqueue */ - ret = wait_event_interruptible_timeout(m_dev->queue, - m_dev->int_cond, msecs_to_jiffies(MFC_INT_TIMEOUT)); - if (ret == 0) { - mfc_err("Waiting for hardware to finish timed out\n"); - clear_bit(0, &m_dev->enter_suspend); - return -EIO; - } - } - - ret = s5p_mfc_sleep(m_dev); - if (ret) { - clear_bit(0, &m_dev->enter_suspend); - clear_bit(0, &m_dev->hw_lock); - } - return ret; -} - -static int s5p_mfc_resume(struct device *dev) -{ - struct s5p_mfc_dev *m_dev = dev_get_drvdata(dev); - - if (m_dev->num_inst == 0) - return 0; - return s5p_mfc_wakeup(m_dev); -} -#endif - -/* Power management */ -static const struct dev_pm_ops s5p_mfc_pm_ops = { - SET_SYSTEM_SLEEP_PM_OPS(s5p_mfc_suspend, s5p_mfc_resume) -}; - -static struct s5p_mfc_buf_size_v5 mfc_buf_size_v5 = { - .h264_ctx = MFC_H264_CTX_BUF_SIZE, - .non_h264_ctx = MFC_CTX_BUF_SIZE, - .dsc = DESC_BUF_SIZE, - .shm = SHARED_BUF_SIZE, -}; - -static struct s5p_mfc_buf_size buf_size_v5 = { - .fw = MAX_FW_SIZE, - .cpb = MAX_CPB_SIZE, - .priv = &mfc_buf_size_v5, -}; - -static struct s5p_mfc_variant mfc_drvdata_v5 = { - .version = MFC_VERSION, - .version_bit = MFC_V5_BIT, - .port_num = MFC_NUM_PORTS, - .buf_size = &buf_size_v5, - .fw_name[0] = "s5p-mfc.fw", - .clk_names = {"mfc", "sclk_mfc"}, - .num_clocks = 2, - .use_clock_gating = true, -}; - -static struct s5p_mfc_buf_size_v6 mfc_buf_size_v6 = { - .dev_ctx = MFC_CTX_BUF_SIZE_V6, - .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V6, - .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V6, - .h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V6, - .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V6, -}; - -static struct s5p_mfc_buf_size buf_size_v6 = { - .fw = MAX_FW_SIZE_V6, - .cpb = MAX_CPB_SIZE_V6, - .priv = &mfc_buf_size_v6, -}; - -static struct s5p_mfc_variant mfc_drvdata_v6 = { - .version = MFC_VERSION_V6, - .version_bit = MFC_V6_BIT, - .port_num = MFC_NUM_PORTS_V6, - .buf_size = &buf_size_v6, - .fw_name[0] = "s5p-mfc-v6.fw", - /* - * v6-v2 firmware contains bug fixes and interface change - * for init buffer command - */ - .fw_name[1] = "s5p-mfc-v6-v2.fw", - .clk_names = {"mfc"}, - .num_clocks = 1, -}; - -static struct s5p_mfc_buf_size_v6 mfc_buf_size_v7 = { - .dev_ctx = MFC_CTX_BUF_SIZE_V7, - .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V7, - .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V7, - .h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V7, - .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V7, -}; - -static struct s5p_mfc_buf_size buf_size_v7 = { - .fw = MAX_FW_SIZE_V7, - .cpb = MAX_CPB_SIZE_V7, - .priv = &mfc_buf_size_v7, -}; - -static struct s5p_mfc_variant mfc_drvdata_v7 = { - .version = MFC_VERSION_V7, - .version_bit = MFC_V7_BIT, - .port_num = MFC_NUM_PORTS_V7, - .buf_size = &buf_size_v7, - .fw_name[0] = "s5p-mfc-v7.fw", - .clk_names = {"mfc", "sclk_mfc"}, - .num_clocks = 2, -}; - -static struct s5p_mfc_buf_size_v6 mfc_buf_size_v8 = { - .dev_ctx = MFC_CTX_BUF_SIZE_V8, - .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V8, - .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V8, - .h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V8, - .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V8, -}; - -static struct s5p_mfc_buf_size buf_size_v8 = { - .fw = MAX_FW_SIZE_V8, - .cpb = MAX_CPB_SIZE_V8, - .priv = &mfc_buf_size_v8, -}; - -static struct s5p_mfc_variant mfc_drvdata_v8 = { - .version = MFC_VERSION_V8, - .version_bit = MFC_V8_BIT, - .port_num = MFC_NUM_PORTS_V8, - .buf_size = &buf_size_v8, - .fw_name[0] = "s5p-mfc-v8.fw", - .clk_names = {"mfc"}, - .num_clocks = 1, -}; - -static struct s5p_mfc_variant mfc_drvdata_v8_5433 = { - .version = MFC_VERSION_V8, - .version_bit = MFC_V8_BIT, - .port_num = MFC_NUM_PORTS_V8, - .buf_size = &buf_size_v8, - .fw_name[0] = "s5p-mfc-v8.fw", - .clk_names = {"pclk", "aclk", "aclk_xiu"}, - .num_clocks = 3, -}; - -static struct s5p_mfc_buf_size_v6 mfc_buf_size_v10 = { - .dev_ctx = MFC_CTX_BUF_SIZE_V10, - .h264_dec_ctx = MFC_H264_DEC_CTX_BUF_SIZE_V10, - .other_dec_ctx = MFC_OTHER_DEC_CTX_BUF_SIZE_V10, - .h264_enc_ctx = MFC_H264_ENC_CTX_BUF_SIZE_V10, - .hevc_enc_ctx = MFC_HEVC_ENC_CTX_BUF_SIZE_V10, - .other_enc_ctx = MFC_OTHER_ENC_CTX_BUF_SIZE_V10, -}; - -static struct s5p_mfc_buf_size buf_size_v10 = { - .fw = MAX_FW_SIZE_V10, - .cpb = MAX_CPB_SIZE_V10, - .priv = &mfc_buf_size_v10, -}; - -static struct s5p_mfc_variant mfc_drvdata_v10 = { - .version = MFC_VERSION_V10, - .version_bit = MFC_V10_BIT, - .port_num = MFC_NUM_PORTS_V10, - .buf_size = &buf_size_v10, - .fw_name[0] = "s5p-mfc-v10.fw", -}; - -static const struct of_device_id exynos_mfc_match[] = { - { - .compatible = "samsung,mfc-v5", - .data = &mfc_drvdata_v5, - }, { - .compatible = "samsung,mfc-v6", - .data = &mfc_drvdata_v6, - }, { - .compatible = "samsung,mfc-v7", - .data = &mfc_drvdata_v7, - }, { - .compatible = "samsung,mfc-v8", - .data = &mfc_drvdata_v8, - }, { - .compatible = "samsung,exynos5433-mfc", - .data = &mfc_drvdata_v8_5433, - }, { - .compatible = "samsung,mfc-v10", - .data = &mfc_drvdata_v10, - }, - {}, -}; -MODULE_DEVICE_TABLE(of, exynos_mfc_match); - -static struct platform_driver s5p_mfc_driver = { - .probe = s5p_mfc_probe, - .remove = s5p_mfc_remove, - .driver = { - .name = S5P_MFC_NAME, - .pm = &s5p_mfc_pm_ops, - .of_match_table = exynos_mfc_match, - }, -}; - -module_platform_driver(s5p_mfc_driver); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>"); -MODULE_DESCRIPTION("Samsung S5P Multi Format Codec V4L2 driver"); - |