18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) STMicroelectronics SA 2015
48c2ecf20Sopenharmony_ci * Authors: Yannick Fertre <yannick.fertre@st.com>
58c2ecf20Sopenharmony_ci *          Hugues Fruchet <hugues.fruchet@st.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/clk.h>
98c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
108c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
118c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
128c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
138c2ecf20Sopenharmony_ci#include <linux/seq_file.h>
148c2ecf20Sopenharmony_ci#endif
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include "hva.h"
178c2ecf20Sopenharmony_ci#include "hva-hw.h"
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci/* HVA register offsets */
208c2ecf20Sopenharmony_ci#define HVA_HIF_REG_RST                 0x0100U
218c2ecf20Sopenharmony_ci#define HVA_HIF_REG_RST_ACK             0x0104U
228c2ecf20Sopenharmony_ci#define HVA_HIF_REG_MIF_CFG             0x0108U
238c2ecf20Sopenharmony_ci#define HVA_HIF_REG_HEC_MIF_CFG         0x010CU
248c2ecf20Sopenharmony_ci#define HVA_HIF_REG_CFL                 0x0110U
258c2ecf20Sopenharmony_ci#define HVA_HIF_FIFO_CMD                0x0114U
268c2ecf20Sopenharmony_ci#define HVA_HIF_FIFO_STS                0x0118U
278c2ecf20Sopenharmony_ci#define HVA_HIF_REG_SFL                 0x011CU
288c2ecf20Sopenharmony_ci#define HVA_HIF_REG_IT_ACK              0x0120U
298c2ecf20Sopenharmony_ci#define HVA_HIF_REG_ERR_IT_ACK          0x0124U
308c2ecf20Sopenharmony_ci#define HVA_HIF_REG_LMI_ERR             0x0128U
318c2ecf20Sopenharmony_ci#define HVA_HIF_REG_EMI_ERR             0x012CU
328c2ecf20Sopenharmony_ci#define HVA_HIF_REG_HEC_MIF_ERR         0x0130U
338c2ecf20Sopenharmony_ci#define HVA_HIF_REG_HEC_STS             0x0134U
348c2ecf20Sopenharmony_ci#define HVA_HIF_REG_HVC_STS             0x0138U
358c2ecf20Sopenharmony_ci#define HVA_HIF_REG_HJE_STS             0x013CU
368c2ecf20Sopenharmony_ci#define HVA_HIF_REG_CNT                 0x0140U
378c2ecf20Sopenharmony_ci#define HVA_HIF_REG_HEC_CHKSYN_DIS      0x0144U
388c2ecf20Sopenharmony_ci#define HVA_HIF_REG_CLK_GATING          0x0148U
398c2ecf20Sopenharmony_ci#define HVA_HIF_REG_VERSION             0x014CU
408c2ecf20Sopenharmony_ci#define HVA_HIF_REG_BSM                 0x0150U
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci/* define value for version id register (HVA_HIF_REG_VERSION) */
438c2ecf20Sopenharmony_ci#define VERSION_ID_MASK	0x0000FFFF
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci/* define values for BSM register (HVA_HIF_REG_BSM) */
468c2ecf20Sopenharmony_ci#define BSM_CFG_VAL1	0x0003F000
478c2ecf20Sopenharmony_ci#define BSM_CFG_VAL2	0x003F0000
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci/* define values for memory interface register (HVA_HIF_REG_MIF_CFG) */
508c2ecf20Sopenharmony_ci#define MIF_CFG_VAL1	0x04460446
518c2ecf20Sopenharmony_ci#define MIF_CFG_VAL2	0x04460806
528c2ecf20Sopenharmony_ci#define MIF_CFG_VAL3	0x00000000
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci/* define value for HEC memory interface register (HVA_HIF_REG_MIF_CFG) */
558c2ecf20Sopenharmony_ci#define HEC_MIF_CFG_VAL	0x000000C4
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci/*  Bits definition for clock gating register (HVA_HIF_REG_CLK_GATING) */
588c2ecf20Sopenharmony_ci#define CLK_GATING_HVC	BIT(0)
598c2ecf20Sopenharmony_ci#define CLK_GATING_HEC	BIT(1)
608c2ecf20Sopenharmony_ci#define CLK_GATING_HJE	BIT(2)
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/* fix hva clock rate */
638c2ecf20Sopenharmony_ci#define CLK_RATE		300000000
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci/* fix delay for pmruntime */
668c2ecf20Sopenharmony_ci#define AUTOSUSPEND_DELAY_MS	3
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci/*
698c2ecf20Sopenharmony_ci * hw encode error values
708c2ecf20Sopenharmony_ci * NO_ERROR: Success, Task OK
718c2ecf20Sopenharmony_ci * H264_BITSTREAM_OVERSIZE: VECH264 Bitstream size > bitstream buffer
728c2ecf20Sopenharmony_ci * H264_FRAME_SKIPPED: VECH264 Frame skipped (refers to CPB Buffer Size)
738c2ecf20Sopenharmony_ci * H264_SLICE_LIMIT_SIZE: VECH264 MB > slice limit size
748c2ecf20Sopenharmony_ci * H264_MAX_SLICE_NUMBER: VECH264 max slice number reached
758c2ecf20Sopenharmony_ci * H264_SLICE_READY: VECH264 Slice ready
768c2ecf20Sopenharmony_ci * TASK_LIST_FULL: HVA/FPC task list full
778c2ecf20Sopenharmony_ci		   (discard latest transform command)
788c2ecf20Sopenharmony_ci * UNKNOWN_COMMAND: Transform command not known by HVA/FPC
798c2ecf20Sopenharmony_ci * WRONG_CODEC_OR_RESOLUTION: Wrong Codec or Resolution Selection
808c2ecf20Sopenharmony_ci * NO_INT_COMPLETION: Time-out on interrupt completion
818c2ecf20Sopenharmony_ci * LMI_ERR: Local Memory Interface Error
828c2ecf20Sopenharmony_ci * EMI_ERR: External Memory Interface Error
838c2ecf20Sopenharmony_ci * HECMI_ERR: HEC Memory Interface Error
848c2ecf20Sopenharmony_ci */
858c2ecf20Sopenharmony_cienum hva_hw_error {
868c2ecf20Sopenharmony_ci	NO_ERROR = 0x0,
878c2ecf20Sopenharmony_ci	H264_BITSTREAM_OVERSIZE = 0x2,
888c2ecf20Sopenharmony_ci	H264_FRAME_SKIPPED = 0x4,
898c2ecf20Sopenharmony_ci	H264_SLICE_LIMIT_SIZE = 0x5,
908c2ecf20Sopenharmony_ci	H264_MAX_SLICE_NUMBER = 0x7,
918c2ecf20Sopenharmony_ci	H264_SLICE_READY = 0x8,
928c2ecf20Sopenharmony_ci	TASK_LIST_FULL = 0xF0,
938c2ecf20Sopenharmony_ci	UNKNOWN_COMMAND = 0xF1,
948c2ecf20Sopenharmony_ci	WRONG_CODEC_OR_RESOLUTION = 0xF4,
958c2ecf20Sopenharmony_ci	NO_INT_COMPLETION = 0x100,
968c2ecf20Sopenharmony_ci	LMI_ERR = 0x101,
978c2ecf20Sopenharmony_ci	EMI_ERR = 0x102,
988c2ecf20Sopenharmony_ci	HECMI_ERR = 0x103,
998c2ecf20Sopenharmony_ci};
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic irqreturn_t hva_hw_its_interrupt(int irq, void *data)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	struct hva_dev *hva = data;
1048c2ecf20Sopenharmony_ci
1058c2ecf20Sopenharmony_ci	/* read status registers */
1068c2ecf20Sopenharmony_ci	hva->sts_reg = readl_relaxed(hva->regs + HVA_HIF_FIFO_STS);
1078c2ecf20Sopenharmony_ci	hva->sfl_reg = readl_relaxed(hva->regs + HVA_HIF_REG_SFL);
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ci	/* acknowledge interruption */
1108c2ecf20Sopenharmony_ci	writel_relaxed(0x1, hva->regs + HVA_HIF_REG_IT_ACK);
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci	return IRQ_WAKE_THREAD;
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_cistatic irqreturn_t hva_hw_its_irq_thread(int irq, void *arg)
1168c2ecf20Sopenharmony_ci{
1178c2ecf20Sopenharmony_ci	struct hva_dev *hva = arg;
1188c2ecf20Sopenharmony_ci	struct device *dev = hva_to_dev(hva);
1198c2ecf20Sopenharmony_ci	u32 status = hva->sts_reg & 0xFF;
1208c2ecf20Sopenharmony_ci	u8 ctx_id = 0;
1218c2ecf20Sopenharmony_ci	struct hva_ctx *ctx = NULL;
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s     %s: status: 0x%02x fifo level: 0x%02x\n",
1248c2ecf20Sopenharmony_ci		HVA_PREFIX, __func__, hva->sts_reg & 0xFF, hva->sfl_reg & 0xF);
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci	/*
1278c2ecf20Sopenharmony_ci	 * status: task_id[31:16] client_id[15:8] status[7:0]
1288c2ecf20Sopenharmony_ci	 * the context identifier is retrieved from the client identifier
1298c2ecf20Sopenharmony_ci	 */
1308c2ecf20Sopenharmony_ci	ctx_id = (hva->sts_reg & 0xFF00) >> 8;
1318c2ecf20Sopenharmony_ci	if (ctx_id >= HVA_MAX_INSTANCES) {
1328c2ecf20Sopenharmony_ci		dev_err(dev, "%s     %s: bad context identifier: %d\n",
1338c2ecf20Sopenharmony_ci			HVA_PREFIX, __func__, ctx_id);
1348c2ecf20Sopenharmony_ci		goto out;
1358c2ecf20Sopenharmony_ci	}
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci	ctx = hva->instances[ctx_id];
1388c2ecf20Sopenharmony_ci	if (!ctx)
1398c2ecf20Sopenharmony_ci		goto out;
1408c2ecf20Sopenharmony_ci
1418c2ecf20Sopenharmony_ci	switch (status) {
1428c2ecf20Sopenharmony_ci	case NO_ERROR:
1438c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s     %s: no error\n",
1448c2ecf20Sopenharmony_ci			ctx->name, __func__);
1458c2ecf20Sopenharmony_ci		ctx->hw_err = false;
1468c2ecf20Sopenharmony_ci		break;
1478c2ecf20Sopenharmony_ci	case H264_SLICE_READY:
1488c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s     %s: h264 slice ready\n",
1498c2ecf20Sopenharmony_ci			ctx->name, __func__);
1508c2ecf20Sopenharmony_ci		ctx->hw_err = false;
1518c2ecf20Sopenharmony_ci		break;
1528c2ecf20Sopenharmony_ci	case H264_FRAME_SKIPPED:
1538c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s     %s: h264 frame skipped\n",
1548c2ecf20Sopenharmony_ci			ctx->name, __func__);
1558c2ecf20Sopenharmony_ci		ctx->hw_err = false;
1568c2ecf20Sopenharmony_ci		break;
1578c2ecf20Sopenharmony_ci	case H264_BITSTREAM_OVERSIZE:
1588c2ecf20Sopenharmony_ci		dev_err(dev, "%s     %s:h264 bitstream oversize\n",
1598c2ecf20Sopenharmony_ci			ctx->name, __func__);
1608c2ecf20Sopenharmony_ci		ctx->hw_err = true;
1618c2ecf20Sopenharmony_ci		break;
1628c2ecf20Sopenharmony_ci	case H264_SLICE_LIMIT_SIZE:
1638c2ecf20Sopenharmony_ci		dev_err(dev, "%s     %s: h264 slice limit size is reached\n",
1648c2ecf20Sopenharmony_ci			ctx->name, __func__);
1658c2ecf20Sopenharmony_ci		ctx->hw_err = true;
1668c2ecf20Sopenharmony_ci		break;
1678c2ecf20Sopenharmony_ci	case H264_MAX_SLICE_NUMBER:
1688c2ecf20Sopenharmony_ci		dev_err(dev, "%s     %s: h264 max slice number is reached\n",
1698c2ecf20Sopenharmony_ci			ctx->name, __func__);
1708c2ecf20Sopenharmony_ci		ctx->hw_err = true;
1718c2ecf20Sopenharmony_ci		break;
1728c2ecf20Sopenharmony_ci	case TASK_LIST_FULL:
1738c2ecf20Sopenharmony_ci		dev_err(dev, "%s     %s:task list full\n",
1748c2ecf20Sopenharmony_ci			ctx->name, __func__);
1758c2ecf20Sopenharmony_ci		ctx->hw_err = true;
1768c2ecf20Sopenharmony_ci		break;
1778c2ecf20Sopenharmony_ci	case UNKNOWN_COMMAND:
1788c2ecf20Sopenharmony_ci		dev_err(dev, "%s     %s: command not known\n",
1798c2ecf20Sopenharmony_ci			ctx->name, __func__);
1808c2ecf20Sopenharmony_ci		ctx->hw_err = true;
1818c2ecf20Sopenharmony_ci		break;
1828c2ecf20Sopenharmony_ci	case WRONG_CODEC_OR_RESOLUTION:
1838c2ecf20Sopenharmony_ci		dev_err(dev, "%s     %s: wrong codec or resolution\n",
1848c2ecf20Sopenharmony_ci			ctx->name, __func__);
1858c2ecf20Sopenharmony_ci		ctx->hw_err = true;
1868c2ecf20Sopenharmony_ci		break;
1878c2ecf20Sopenharmony_ci	default:
1888c2ecf20Sopenharmony_ci		dev_err(dev, "%s     %s: status not recognized\n",
1898c2ecf20Sopenharmony_ci			ctx->name, __func__);
1908c2ecf20Sopenharmony_ci		ctx->hw_err = true;
1918c2ecf20Sopenharmony_ci		break;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ciout:
1948c2ecf20Sopenharmony_ci	complete(&hva->interrupt);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1978c2ecf20Sopenharmony_ci}
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic irqreturn_t hva_hw_err_interrupt(int irq, void *data)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	struct hva_dev *hva = data;
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci	/* read status registers */
2048c2ecf20Sopenharmony_ci	hva->sts_reg = readl_relaxed(hva->regs + HVA_HIF_FIFO_STS);
2058c2ecf20Sopenharmony_ci	hva->sfl_reg = readl_relaxed(hva->regs + HVA_HIF_REG_SFL);
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	/* read error registers */
2088c2ecf20Sopenharmony_ci	hva->lmi_err_reg = readl_relaxed(hva->regs + HVA_HIF_REG_LMI_ERR);
2098c2ecf20Sopenharmony_ci	hva->emi_err_reg = readl_relaxed(hva->regs + HVA_HIF_REG_EMI_ERR);
2108c2ecf20Sopenharmony_ci	hva->hec_mif_err_reg = readl_relaxed(hva->regs +
2118c2ecf20Sopenharmony_ci					     HVA_HIF_REG_HEC_MIF_ERR);
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	/* acknowledge interruption */
2148c2ecf20Sopenharmony_ci	writel_relaxed(0x1, hva->regs + HVA_HIF_REG_IT_ACK);
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci	return IRQ_WAKE_THREAD;
2178c2ecf20Sopenharmony_ci}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_cistatic irqreturn_t hva_hw_err_irq_thread(int irq, void *arg)
2208c2ecf20Sopenharmony_ci{
2218c2ecf20Sopenharmony_ci	struct hva_dev *hva = arg;
2228c2ecf20Sopenharmony_ci	struct device *dev = hva_to_dev(hva);
2238c2ecf20Sopenharmony_ci	u8 ctx_id = 0;
2248c2ecf20Sopenharmony_ci	struct hva_ctx *ctx;
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s     status: 0x%02x fifo level: 0x%02x\n",
2278c2ecf20Sopenharmony_ci		HVA_PREFIX, hva->sts_reg & 0xFF, hva->sfl_reg & 0xF);
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci	/*
2308c2ecf20Sopenharmony_ci	 * status: task_id[31:16] client_id[15:8] status[7:0]
2318c2ecf20Sopenharmony_ci	 * the context identifier is retrieved from the client identifier
2328c2ecf20Sopenharmony_ci	 */
2338c2ecf20Sopenharmony_ci	ctx_id = (hva->sts_reg & 0xFF00) >> 8;
2348c2ecf20Sopenharmony_ci	if (ctx_id >= HVA_MAX_INSTANCES) {
2358c2ecf20Sopenharmony_ci		dev_err(dev, "%s     bad context identifier: %d\n", HVA_PREFIX,
2368c2ecf20Sopenharmony_ci			ctx_id);
2378c2ecf20Sopenharmony_ci		goto out;
2388c2ecf20Sopenharmony_ci	}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	ctx = hva->instances[ctx_id];
2418c2ecf20Sopenharmony_ci	if (!ctx)
2428c2ecf20Sopenharmony_ci		goto out;
2438c2ecf20Sopenharmony_ci
2448c2ecf20Sopenharmony_ci	if (hva->lmi_err_reg) {
2458c2ecf20Sopenharmony_ci		dev_err(dev, "%s     local memory interface error: 0x%08x\n",
2468c2ecf20Sopenharmony_ci			ctx->name, hva->lmi_err_reg);
2478c2ecf20Sopenharmony_ci		ctx->hw_err = true;
2488c2ecf20Sopenharmony_ci	}
2498c2ecf20Sopenharmony_ci
2508c2ecf20Sopenharmony_ci	if (hva->emi_err_reg) {
2518c2ecf20Sopenharmony_ci		dev_err(dev, "%s     external memory interface error: 0x%08x\n",
2528c2ecf20Sopenharmony_ci			ctx->name, hva->emi_err_reg);
2538c2ecf20Sopenharmony_ci		ctx->hw_err = true;
2548c2ecf20Sopenharmony_ci	}
2558c2ecf20Sopenharmony_ci
2568c2ecf20Sopenharmony_ci	if (hva->hec_mif_err_reg) {
2578c2ecf20Sopenharmony_ci		dev_err(dev, "%s     hec memory interface error: 0x%08x\n",
2588c2ecf20Sopenharmony_ci			ctx->name, hva->hec_mif_err_reg);
2598c2ecf20Sopenharmony_ci		ctx->hw_err = true;
2608c2ecf20Sopenharmony_ci	}
2618c2ecf20Sopenharmony_ciout:
2628c2ecf20Sopenharmony_ci	complete(&hva->interrupt);
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
2658c2ecf20Sopenharmony_ci}
2668c2ecf20Sopenharmony_ci
2678c2ecf20Sopenharmony_cistatic unsigned long int hva_hw_get_ip_version(struct hva_dev *hva)
2688c2ecf20Sopenharmony_ci{
2698c2ecf20Sopenharmony_ci	struct device *dev = hva_to_dev(hva);
2708c2ecf20Sopenharmony_ci	unsigned long int version;
2718c2ecf20Sopenharmony_ci
2728c2ecf20Sopenharmony_ci	if (pm_runtime_get_sync(dev) < 0) {
2738c2ecf20Sopenharmony_ci		dev_err(dev, "%s     failed to get pm_runtime\n", HVA_PREFIX);
2748c2ecf20Sopenharmony_ci		pm_runtime_put_noidle(dev);
2758c2ecf20Sopenharmony_ci		mutex_unlock(&hva->protect_mutex);
2768c2ecf20Sopenharmony_ci		return -EFAULT;
2778c2ecf20Sopenharmony_ci	}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	version = readl_relaxed(hva->regs + HVA_HIF_REG_VERSION) &
2808c2ecf20Sopenharmony_ci				VERSION_ID_MASK;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
2838c2ecf20Sopenharmony_ci
2848c2ecf20Sopenharmony_ci	switch (version) {
2858c2ecf20Sopenharmony_ci	case HVA_VERSION_V400:
2868c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s     IP hardware version 0x%lx\n",
2878c2ecf20Sopenharmony_ci			HVA_PREFIX, version);
2888c2ecf20Sopenharmony_ci		break;
2898c2ecf20Sopenharmony_ci	default:
2908c2ecf20Sopenharmony_ci		dev_err(dev, "%s     unknown IP hardware version 0x%lx\n",
2918c2ecf20Sopenharmony_ci			HVA_PREFIX, version);
2928c2ecf20Sopenharmony_ci		version = HVA_VERSION_UNKNOWN;
2938c2ecf20Sopenharmony_ci		break;
2948c2ecf20Sopenharmony_ci	}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci	return version;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci
2998c2ecf20Sopenharmony_ciint hva_hw_probe(struct platform_device *pdev, struct hva_dev *hva)
3008c2ecf20Sopenharmony_ci{
3018c2ecf20Sopenharmony_ci	struct device *dev = &pdev->dev;
3028c2ecf20Sopenharmony_ci	struct resource *regs;
3038c2ecf20Sopenharmony_ci	struct resource *esram;
3048c2ecf20Sopenharmony_ci	int ret;
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci	WARN_ON(!hva);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci	/* get memory for registers */
3098c2ecf20Sopenharmony_ci	regs = platform_get_resource(pdev, IORESOURCE_MEM, 0);
3108c2ecf20Sopenharmony_ci	hva->regs = devm_ioremap_resource(dev, regs);
3118c2ecf20Sopenharmony_ci	if (IS_ERR(hva->regs)) {
3128c2ecf20Sopenharmony_ci		dev_err(dev, "%s     failed to get regs\n", HVA_PREFIX);
3138c2ecf20Sopenharmony_ci		return PTR_ERR(hva->regs);
3148c2ecf20Sopenharmony_ci	}
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	/* get memory for esram */
3178c2ecf20Sopenharmony_ci	esram = platform_get_resource(pdev, IORESOURCE_MEM, 1);
3188c2ecf20Sopenharmony_ci	if (!esram) {
3198c2ecf20Sopenharmony_ci		dev_err(dev, "%s     failed to get esram\n", HVA_PREFIX);
3208c2ecf20Sopenharmony_ci		return -ENODEV;
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci	hva->esram_addr = esram->start;
3238c2ecf20Sopenharmony_ci	hva->esram_size = resource_size(esram);
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	dev_info(dev, "%s     esram reserved for address: 0x%x size:%d\n",
3268c2ecf20Sopenharmony_ci		 HVA_PREFIX, hva->esram_addr, hva->esram_size);
3278c2ecf20Sopenharmony_ci
3288c2ecf20Sopenharmony_ci	/* get clock resource */
3298c2ecf20Sopenharmony_ci	hva->clk = devm_clk_get(dev, "clk_hva");
3308c2ecf20Sopenharmony_ci	if (IS_ERR(hva->clk)) {
3318c2ecf20Sopenharmony_ci		dev_err(dev, "%s     failed to get clock\n", HVA_PREFIX);
3328c2ecf20Sopenharmony_ci		return PTR_ERR(hva->clk);
3338c2ecf20Sopenharmony_ci	}
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	ret = clk_prepare(hva->clk);
3368c2ecf20Sopenharmony_ci	if (ret < 0) {
3378c2ecf20Sopenharmony_ci		dev_err(dev, "%s     failed to prepare clock\n", HVA_PREFIX);
3388c2ecf20Sopenharmony_ci		hva->clk = ERR_PTR(-EINVAL);
3398c2ecf20Sopenharmony_ci		return ret;
3408c2ecf20Sopenharmony_ci	}
3418c2ecf20Sopenharmony_ci
3428c2ecf20Sopenharmony_ci	/* get status interruption resource */
3438c2ecf20Sopenharmony_ci	ret  = platform_get_irq(pdev, 0);
3448c2ecf20Sopenharmony_ci	if (ret < 0)
3458c2ecf20Sopenharmony_ci		goto err_clk;
3468c2ecf20Sopenharmony_ci	hva->irq_its = ret;
3478c2ecf20Sopenharmony_ci
3488c2ecf20Sopenharmony_ci	ret = devm_request_threaded_irq(dev, hva->irq_its, hva_hw_its_interrupt,
3498c2ecf20Sopenharmony_ci					hva_hw_its_irq_thread,
3508c2ecf20Sopenharmony_ci					IRQF_ONESHOT,
3518c2ecf20Sopenharmony_ci					"hva_its_irq", hva);
3528c2ecf20Sopenharmony_ci	if (ret) {
3538c2ecf20Sopenharmony_ci		dev_err(dev, "%s     failed to install status IRQ 0x%x\n",
3548c2ecf20Sopenharmony_ci			HVA_PREFIX, hva->irq_its);
3558c2ecf20Sopenharmony_ci		goto err_clk;
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci	disable_irq(hva->irq_its);
3588c2ecf20Sopenharmony_ci
3598c2ecf20Sopenharmony_ci	/* get error interruption resource */
3608c2ecf20Sopenharmony_ci	ret = platform_get_irq(pdev, 1);
3618c2ecf20Sopenharmony_ci	if (ret < 0)
3628c2ecf20Sopenharmony_ci		goto err_clk;
3638c2ecf20Sopenharmony_ci	hva->irq_err = ret;
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	ret = devm_request_threaded_irq(dev, hva->irq_err, hva_hw_err_interrupt,
3668c2ecf20Sopenharmony_ci					hva_hw_err_irq_thread,
3678c2ecf20Sopenharmony_ci					IRQF_ONESHOT,
3688c2ecf20Sopenharmony_ci					"hva_err_irq", hva);
3698c2ecf20Sopenharmony_ci	if (ret) {
3708c2ecf20Sopenharmony_ci		dev_err(dev, "%s     failed to install error IRQ 0x%x\n",
3718c2ecf20Sopenharmony_ci			HVA_PREFIX, hva->irq_err);
3728c2ecf20Sopenharmony_ci		goto err_clk;
3738c2ecf20Sopenharmony_ci	}
3748c2ecf20Sopenharmony_ci	disable_irq(hva->irq_err);
3758c2ecf20Sopenharmony_ci
3768c2ecf20Sopenharmony_ci	/* initialise protection mutex */
3778c2ecf20Sopenharmony_ci	mutex_init(&hva->protect_mutex);
3788c2ecf20Sopenharmony_ci
3798c2ecf20Sopenharmony_ci	/* initialise completion signal */
3808c2ecf20Sopenharmony_ci	init_completion(&hva->interrupt);
3818c2ecf20Sopenharmony_ci
3828c2ecf20Sopenharmony_ci	/* initialise runtime power management */
3838c2ecf20Sopenharmony_ci	pm_runtime_set_autosuspend_delay(dev, AUTOSUSPEND_DELAY_MS);
3848c2ecf20Sopenharmony_ci	pm_runtime_use_autosuspend(dev);
3858c2ecf20Sopenharmony_ci	pm_runtime_set_suspended(dev);
3868c2ecf20Sopenharmony_ci	pm_runtime_enable(dev);
3878c2ecf20Sopenharmony_ci
3888c2ecf20Sopenharmony_ci	ret = pm_runtime_get_sync(dev);
3898c2ecf20Sopenharmony_ci	if (ret < 0) {
3908c2ecf20Sopenharmony_ci		dev_err(dev, "%s     failed to set PM\n", HVA_PREFIX);
3918c2ecf20Sopenharmony_ci		goto err_pm;
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci
3948c2ecf20Sopenharmony_ci	/* check IP hardware version */
3958c2ecf20Sopenharmony_ci	hva->ip_version = hva_hw_get_ip_version(hva);
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci	if (hva->ip_version == HVA_VERSION_UNKNOWN) {
3988c2ecf20Sopenharmony_ci		ret = -EINVAL;
3998c2ecf20Sopenharmony_ci		goto err_pm;
4008c2ecf20Sopenharmony_ci	}
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	dev_info(dev, "%s     found hva device (version 0x%lx)\n", HVA_PREFIX,
4038c2ecf20Sopenharmony_ci		 hva->ip_version);
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	return 0;
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_cierr_pm:
4088c2ecf20Sopenharmony_ci	pm_runtime_put(dev);
4098c2ecf20Sopenharmony_cierr_clk:
4108c2ecf20Sopenharmony_ci	if (hva->clk)
4118c2ecf20Sopenharmony_ci		clk_unprepare(hva->clk);
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	return ret;
4148c2ecf20Sopenharmony_ci}
4158c2ecf20Sopenharmony_ci
4168c2ecf20Sopenharmony_civoid hva_hw_remove(struct hva_dev *hva)
4178c2ecf20Sopenharmony_ci{
4188c2ecf20Sopenharmony_ci	struct device *dev = hva_to_dev(hva);
4198c2ecf20Sopenharmony_ci
4208c2ecf20Sopenharmony_ci	disable_irq(hva->irq_its);
4218c2ecf20Sopenharmony_ci	disable_irq(hva->irq_err);
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
4248c2ecf20Sopenharmony_ci	pm_runtime_disable(dev);
4258c2ecf20Sopenharmony_ci}
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ciint hva_hw_runtime_suspend(struct device *dev)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	struct hva_dev *hva = dev_get_drvdata(dev);
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	clk_disable_unprepare(hva->clk);
4328c2ecf20Sopenharmony_ci
4338c2ecf20Sopenharmony_ci	return 0;
4348c2ecf20Sopenharmony_ci}
4358c2ecf20Sopenharmony_ci
4368c2ecf20Sopenharmony_ciint hva_hw_runtime_resume(struct device *dev)
4378c2ecf20Sopenharmony_ci{
4388c2ecf20Sopenharmony_ci	struct hva_dev *hva = dev_get_drvdata(dev);
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	if (clk_prepare_enable(hva->clk)) {
4418c2ecf20Sopenharmony_ci		dev_err(hva->dev, "%s     failed to prepare hva clk\n",
4428c2ecf20Sopenharmony_ci			HVA_PREFIX);
4438c2ecf20Sopenharmony_ci		return -EINVAL;
4448c2ecf20Sopenharmony_ci	}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_ci	if (clk_set_rate(hva->clk, CLK_RATE)) {
4478c2ecf20Sopenharmony_ci		dev_err(dev, "%s     failed to set clock frequency\n",
4488c2ecf20Sopenharmony_ci			HVA_PREFIX);
4498c2ecf20Sopenharmony_ci		return -EINVAL;
4508c2ecf20Sopenharmony_ci	}
4518c2ecf20Sopenharmony_ci
4528c2ecf20Sopenharmony_ci	return 0;
4538c2ecf20Sopenharmony_ci}
4548c2ecf20Sopenharmony_ci
4558c2ecf20Sopenharmony_ciint hva_hw_execute_task(struct hva_ctx *ctx, enum hva_hw_cmd_type cmd,
4568c2ecf20Sopenharmony_ci			struct hva_buffer *task)
4578c2ecf20Sopenharmony_ci{
4588c2ecf20Sopenharmony_ci	struct hva_dev *hva = ctx_to_hdev(ctx);
4598c2ecf20Sopenharmony_ci	struct device *dev = hva_to_dev(hva);
4608c2ecf20Sopenharmony_ci	u8 client_id = ctx->id;
4618c2ecf20Sopenharmony_ci	int ret;
4628c2ecf20Sopenharmony_ci	u32 reg = 0;
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_ci	mutex_lock(&hva->protect_mutex);
4658c2ecf20Sopenharmony_ci
4668c2ecf20Sopenharmony_ci	/* enable irqs */
4678c2ecf20Sopenharmony_ci	enable_irq(hva->irq_its);
4688c2ecf20Sopenharmony_ci	enable_irq(hva->irq_err);
4698c2ecf20Sopenharmony_ci
4708c2ecf20Sopenharmony_ci	if (pm_runtime_get_sync(dev) < 0) {
4718c2ecf20Sopenharmony_ci		dev_err(dev, "%s     failed to get pm_runtime\n", ctx->name);
4728c2ecf20Sopenharmony_ci		ctx->sys_errors++;
4738c2ecf20Sopenharmony_ci		ret = -EFAULT;
4748c2ecf20Sopenharmony_ci		goto out;
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	reg = readl_relaxed(hva->regs + HVA_HIF_REG_CLK_GATING);
4788c2ecf20Sopenharmony_ci	switch (cmd) {
4798c2ecf20Sopenharmony_ci	case H264_ENC:
4808c2ecf20Sopenharmony_ci		reg |= CLK_GATING_HVC;
4818c2ecf20Sopenharmony_ci		break;
4828c2ecf20Sopenharmony_ci	default:
4838c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s     unknown command 0x%x\n", ctx->name, cmd);
4848c2ecf20Sopenharmony_ci		ctx->encode_errors++;
4858c2ecf20Sopenharmony_ci		ret = -EFAULT;
4868c2ecf20Sopenharmony_ci		goto out;
4878c2ecf20Sopenharmony_ci	}
4888c2ecf20Sopenharmony_ci	writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING);
4898c2ecf20Sopenharmony_ci
4908c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s     %s: write configuration registers\n", ctx->name,
4918c2ecf20Sopenharmony_ci		__func__);
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	/* byte swap config */
4948c2ecf20Sopenharmony_ci	writel_relaxed(BSM_CFG_VAL1, hva->regs + HVA_HIF_REG_BSM);
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	/* define Max Opcode Size and Max Message Size for LMI and EMI */
4978c2ecf20Sopenharmony_ci	writel_relaxed(MIF_CFG_VAL3, hva->regs + HVA_HIF_REG_MIF_CFG);
4988c2ecf20Sopenharmony_ci	writel_relaxed(HEC_MIF_CFG_VAL, hva->regs + HVA_HIF_REG_HEC_MIF_CFG);
4998c2ecf20Sopenharmony_ci
5008c2ecf20Sopenharmony_ci	/*
5018c2ecf20Sopenharmony_ci	 * command FIFO: task_id[31:16] client_id[15:8] command_type[7:0]
5028c2ecf20Sopenharmony_ci	 * the context identifier is provided as client identifier to the
5038c2ecf20Sopenharmony_ci	 * hardware, and is retrieved in the interrupt functions from the
5048c2ecf20Sopenharmony_ci	 * status register
5058c2ecf20Sopenharmony_ci	 */
5068c2ecf20Sopenharmony_ci	dev_dbg(dev, "%s     %s: send task (cmd: %d, task_desc: %pad)\n",
5078c2ecf20Sopenharmony_ci		ctx->name, __func__, cmd + (client_id << 8), &task->paddr);
5088c2ecf20Sopenharmony_ci	writel_relaxed(cmd + (client_id << 8), hva->regs + HVA_HIF_FIFO_CMD);
5098c2ecf20Sopenharmony_ci	writel_relaxed(task->paddr, hva->regs + HVA_HIF_FIFO_CMD);
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	if (!wait_for_completion_timeout(&hva->interrupt,
5128c2ecf20Sopenharmony_ci					 msecs_to_jiffies(2000))) {
5138c2ecf20Sopenharmony_ci		dev_err(dev, "%s     %s: time out on completion\n", ctx->name,
5148c2ecf20Sopenharmony_ci			__func__);
5158c2ecf20Sopenharmony_ci		ctx->encode_errors++;
5168c2ecf20Sopenharmony_ci		ret = -EFAULT;
5178c2ecf20Sopenharmony_ci		goto out;
5188c2ecf20Sopenharmony_ci	}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_ci	/* get encoding status */
5218c2ecf20Sopenharmony_ci	ret = ctx->hw_err ? -EFAULT : 0;
5228c2ecf20Sopenharmony_ci
5238c2ecf20Sopenharmony_ci	ctx->encode_errors += ctx->hw_err ? 1 : 0;
5248c2ecf20Sopenharmony_ci
5258c2ecf20Sopenharmony_ciout:
5268c2ecf20Sopenharmony_ci	disable_irq(hva->irq_its);
5278c2ecf20Sopenharmony_ci	disable_irq(hva->irq_err);
5288c2ecf20Sopenharmony_ci
5298c2ecf20Sopenharmony_ci	switch (cmd) {
5308c2ecf20Sopenharmony_ci	case H264_ENC:
5318c2ecf20Sopenharmony_ci		reg &= ~CLK_GATING_HVC;
5328c2ecf20Sopenharmony_ci		writel_relaxed(reg, hva->regs + HVA_HIF_REG_CLK_GATING);
5338c2ecf20Sopenharmony_ci		break;
5348c2ecf20Sopenharmony_ci	default:
5358c2ecf20Sopenharmony_ci		dev_dbg(dev, "%s     unknown command 0x%x\n", ctx->name, cmd);
5368c2ecf20Sopenharmony_ci	}
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
5398c2ecf20Sopenharmony_ci	mutex_unlock(&hva->protect_mutex);
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	return ret;
5428c2ecf20Sopenharmony_ci}
5438c2ecf20Sopenharmony_ci
5448c2ecf20Sopenharmony_ci#ifdef CONFIG_VIDEO_STI_HVA_DEBUGFS
5458c2ecf20Sopenharmony_ci#define DUMP(reg) seq_printf(s, "%-30s: 0x%08X\n",\
5468c2ecf20Sopenharmony_ci			     #reg, readl_relaxed(hva->regs + reg))
5478c2ecf20Sopenharmony_ci
5488c2ecf20Sopenharmony_civoid hva_hw_dump_regs(struct hva_dev *hva, struct seq_file *s)
5498c2ecf20Sopenharmony_ci{
5508c2ecf20Sopenharmony_ci	struct device *dev = hva_to_dev(hva);
5518c2ecf20Sopenharmony_ci
5528c2ecf20Sopenharmony_ci	mutex_lock(&hva->protect_mutex);
5538c2ecf20Sopenharmony_ci
5548c2ecf20Sopenharmony_ci	if (pm_runtime_get_sync(dev) < 0) {
5558c2ecf20Sopenharmony_ci		seq_puts(s, "Cannot wake up IP\n");
5568c2ecf20Sopenharmony_ci		pm_runtime_put_noidle(dev);
5578c2ecf20Sopenharmony_ci		mutex_unlock(&hva->protect_mutex);
5588c2ecf20Sopenharmony_ci		return;
5598c2ecf20Sopenharmony_ci	}
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	seq_printf(s, "Registers:\nReg @ = 0x%p\n", hva->regs);
5628c2ecf20Sopenharmony_ci
5638c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_RST);
5648c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_RST_ACK);
5658c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_MIF_CFG);
5668c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_HEC_MIF_CFG);
5678c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_CFL);
5688c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_SFL);
5698c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_LMI_ERR);
5708c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_EMI_ERR);
5718c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_HEC_MIF_ERR);
5728c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_HEC_STS);
5738c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_HVC_STS);
5748c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_HJE_STS);
5758c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_CNT);
5768c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_HEC_CHKSYN_DIS);
5778c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_CLK_GATING);
5788c2ecf20Sopenharmony_ci	DUMP(HVA_HIF_REG_VERSION);
5798c2ecf20Sopenharmony_ci
5808c2ecf20Sopenharmony_ci	pm_runtime_put_autosuspend(dev);
5818c2ecf20Sopenharmony_ci	mutex_unlock(&hva->protect_mutex);
5828c2ecf20Sopenharmony_ci}
5838c2ecf20Sopenharmony_ci#endif
584