// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
/*
 * Copyright (c) 2020 MediaTek Inc.
 */

#include <media/dvb_demux.h>

#include "mdrv_demux_common.h"
#include "mdrv_demux_debug.h"
#include "mdrv_demux_dvb.h"
#include "mdrv_demux_tsio.h"
#include "mdrv_demux_filter.h"
#include "mdrv_mvd_parser.h"
#include "mdrv_demux_internal_if.h"

int mdrv_demux_get_filter_id(struct dmx_query_info info)
{
	int i, ret;
	struct mdrv_demux_dvb *dvb;
	struct dvb_demux *demux;
	struct dvb_demux_feed *feed;
	enum dmx_ts_pes pes_type[4] = {
		DMX_PES_VIDEO0, DMX_PES_VIDEO1,
		DMX_PES_VIDEO2, DMX_PES_VIDEO3
	};

	ret = mdrv_demux_dvb_get_instance(&dvb);
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] get dvb instance fail\n", __func__, __LINE__);
		return -EFAULT;
	}

	if (info.path_id >= dvb->resource.hwcaps->livein_num) {
		DEMUX_DEBUG_ERR("[%s][%d] path_id: out-of-range\n", __func__, __LINE__);
		return -EINVAL;
	}

	if (info.pid >= MAX_PID) {
		DEMUX_DEBUG_ERR("[%s][%d] pid: out-of-range\n", __func__, __LINE__);
		return -EINVAL;
	}

	if ((info.purpose != PURPOSE_PLAYBACK) && (info.purpose != PURPOSE_RECORD)) {
		DEMUX_DEBUG_ERR("[%s][%d] currently only support DTV case\n",
			__func__, __LINE__);
		return -EINVAL;
	}

	demux = &dvb->paths[info.path_id].demux;
	if (!demux) {
		DEMUX_DEBUG_ERR("[%s][%d] demux is NULL\n", __func__, __LINE__);
		return -EFAULT;
	}

	mutex_lock(&demux->mutex);

	if (info.purpose == PURPOSE_PLAYBACK) {
		list_for_each_entry(feed, &demux->feed_list, list_head) {
			if ((feed->pid == info.pid) &&
				(feed->ts_type & TS_DECODER)) {
				ret = feed->filter->index;
				mutex_unlock(&demux->mutex);
				return ret;
			}
		}
	} else if (info.purpose == PURPOSE_RECORD) {
		list_for_each_entry(feed, &demux->feed_list, list_head) {
			if ((feed->pid == info.pid) &&
				(feed->ts_type == TS_PACKET)) {
				ret = feed->filter->index;
				mutex_unlock(&demux->mutex);
				return ret;
			}
		}
	}

	mutex_unlock(&demux->mutex);
	return -EFAULT;
}

int mdrv_demux_set_ca_ctrl(struct dmx_ca_info info)
{
	int ret;
	struct mdrv_demux_dvb *dvb;
	struct mdrv_demux_tsio *tsio;

	ret = mdrv_demux_dvb_get_instance(&dvb);
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] get dvb instance fail\n", __func__, __LINE__);
		return -EFAULT;
	}

	if (info.path_id >= dvb->resource.hwcaps->livein_num) {
		DEMUX_DEBUG_ERR("[%s][%d] path_id: out-of-range\n", __func__, __LINE__);
		return -EINVAL;
	}

	tsio = &dvb->tsp.tsio;
	if (info.pass_through)
		ret = mdrv_demux_tsio_connect_ca(tsio, info.path_id, info.ca_eng_id);
	else
		ret = mdrv_demux_tsio_disconnect_ca(tsio, info.path_id, info.ca_eng_id);

	return ret;
}

int mdrv_demux_set_secure_mode(struct dmx_secure_info info)
{
	int ret;
	unsigned long flags;
	struct mdrv_demux_dvb *dvb;
	struct mdrv_demux_filter *filter;
	enum pid_filter_type filter_type;
	u16 eng_id, path_id;
	parser_id_enum parser_id;
	vedio_parser_ctrl p_ctrl;

	ret = mdrv_demux_dvb_get_instance(&dvb);
	if (ret < 0) {
		DEMUX_DEBUG_ERR("[%s][%d] get dvb instance fail\n", __func__, __LINE__);
		return -EFAULT;
	}

	if (info.filter_id >= dvb->resource.hwcaps->pid_filter_num) {
		DEMUX_DEBUG_ERR("[%s][%d] filter_id: out-of-range\n", __func__, __LINE__);
		return -EINVAL;
	}

	filter = &dvb->tsp.filter;

	spin_lock_irqsave(&filter->pid_filter_lock, flags);
	filter_type = filter->pid_filter_slot_map[info.filter_id].filter_type;
	eng_id = filter->pid_filter_slot_map[info.filter_id].dst_eng_id;
	path_id = filter->pid_filter_slot_map[info.filter_id].src_path;
	spin_unlock_irqrestore(&filter->pid_filter_lock, flags);

	switch (filter_type) {
	case PID_FILTER_TYPE_VIDEO:
		{
			parser_id = PARSER_ID_VIDEO_BASE + eng_id;

			memset(&p_ctrl, 0, sizeof(vedio_parser_ctrl));
			p_ctrl.size = sizeof(u32);
			p_ctrl.value = (info.enable) ? info.pipeline_id : 0;

			ret = video_parser_set_control(parser_id, VPAS_CMD_SET_SECURE, &p_ctrl);
		}
		break;
	case PID_FILTER_TYPE_PVR:
		{
			enum pvr_data_trans_mode mode =
				(info.enable) ? PVR_DATA_TRANS_TEE_MODE : PVR_DATA_TRANS_REE_MODE;

			ret = mdrv_demux_pvr_set_strategy_mode(&dvb->tsp.pvr, path_id, mode);
		}
		break;
	default:
		DEMUX_DEBUG_ERR("[%s][%d] currently only support video/pvr case\n",
			__func__, __LINE__);
		return -EINVAL;
	}

	return ret;
}
