13d0407baSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
23d0407baSopenharmony_ci/*
33d0407baSopenharmony_ci * Rockchip dmc common functions.
43d0407baSopenharmony_ci *
53d0407baSopenharmony_ci * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
63d0407baSopenharmony_ci * Author: Finley Xiao <finley.xiao@rock-chips.com>
73d0407baSopenharmony_ci */
83d0407baSopenharmony_ci
93d0407baSopenharmony_ci#include <linux/module.h>
103d0407baSopenharmony_ci#include <soc/rockchip/rockchip_dmc.h>
113d0407baSopenharmony_ci
123d0407baSopenharmony_ci#define msch_rl_to_dmcfreq(work) container_of(to_delayed_work(work), \
133d0407baSopenharmony_ci					      struct rockchip_dmcfreq, \
143d0407baSopenharmony_ci					      msch_rl_work)
153d0407baSopenharmony_ci#define MSCH_RL_DELAY_TIME	50 /* ms */
163d0407baSopenharmony_ci
173d0407baSopenharmony_cistatic struct dmcfreq_common_info *common_info;
183d0407baSopenharmony_cistatic DECLARE_RWSEM(rockchip_dmcfreq_sem);
193d0407baSopenharmony_ci
203d0407baSopenharmony_civoid rockchip_dmcfreq_lock(void)
213d0407baSopenharmony_ci{
223d0407baSopenharmony_ci	down_read(&rockchip_dmcfreq_sem);
233d0407baSopenharmony_ci}
243d0407baSopenharmony_ciEXPORT_SYMBOL(rockchip_dmcfreq_lock);
253d0407baSopenharmony_ci
263d0407baSopenharmony_civoid rockchip_dmcfreq_lock_nested(void)
273d0407baSopenharmony_ci{
283d0407baSopenharmony_ci	down_read_nested(&rockchip_dmcfreq_sem, SINGLE_DEPTH_NESTING);
293d0407baSopenharmony_ci}
303d0407baSopenharmony_ciEXPORT_SYMBOL(rockchip_dmcfreq_lock_nested);
313d0407baSopenharmony_ci
323d0407baSopenharmony_civoid rockchip_dmcfreq_unlock(void)
333d0407baSopenharmony_ci{
343d0407baSopenharmony_ci	up_read(&rockchip_dmcfreq_sem);
353d0407baSopenharmony_ci}
363d0407baSopenharmony_ciEXPORT_SYMBOL(rockchip_dmcfreq_unlock);
373d0407baSopenharmony_ci
383d0407baSopenharmony_ciint rockchip_dmcfreq_write_trylock(void)
393d0407baSopenharmony_ci{
403d0407baSopenharmony_ci	return down_write_trylock(&rockchip_dmcfreq_sem);
413d0407baSopenharmony_ci}
423d0407baSopenharmony_ciEXPORT_SYMBOL(rockchip_dmcfreq_write_trylock);
433d0407baSopenharmony_ci
443d0407baSopenharmony_civoid rockchip_dmcfreq_write_unlock(void)
453d0407baSopenharmony_ci{
463d0407baSopenharmony_ci	up_write(&rockchip_dmcfreq_sem);
473d0407baSopenharmony_ci}
483d0407baSopenharmony_ciEXPORT_SYMBOL(rockchip_dmcfreq_write_unlock);
493d0407baSopenharmony_ci
503d0407baSopenharmony_cistatic void set_msch_rl(unsigned int readlatency)
513d0407baSopenharmony_ci
523d0407baSopenharmony_ci{
533d0407baSopenharmony_ci	rockchip_dmcfreq_lock();
543d0407baSopenharmony_ci	dev_dbg(common_info->dev, "rl 0x%x -> 0x%x\n",
553d0407baSopenharmony_ci		common_info->read_latency, readlatency);
563d0407baSopenharmony_ci	if (!common_info->set_msch_readlatency(readlatency))
573d0407baSopenharmony_ci		common_info->read_latency = readlatency;
583d0407baSopenharmony_ci	else
593d0407baSopenharmony_ci		dev_err(common_info->dev, "failed to set msch rl\n");
603d0407baSopenharmony_ci	rockchip_dmcfreq_unlock();
613d0407baSopenharmony_ci}
623d0407baSopenharmony_ci
633d0407baSopenharmony_cistatic void set_msch_rl_work(struct work_struct *work)
643d0407baSopenharmony_ci{
653d0407baSopenharmony_ci	set_msch_rl(0);
663d0407baSopenharmony_ci	common_info->is_msch_rl_work_started = false;
673d0407baSopenharmony_ci}
683d0407baSopenharmony_ci
693d0407baSopenharmony_ciint rockchip_dmcfreq_vop_bandwidth_init(struct dmcfreq_common_info *info)
703d0407baSopenharmony_ci{
713d0407baSopenharmony_ci	if (info->set_msch_readlatency)
723d0407baSopenharmony_ci		INIT_DELAYED_WORK(&info->msch_rl_work, set_msch_rl_work);
733d0407baSopenharmony_ci	common_info = info;
743d0407baSopenharmony_ci
753d0407baSopenharmony_ci	return 0;
763d0407baSopenharmony_ci}
773d0407baSopenharmony_ciEXPORT_SYMBOL(rockchip_dmcfreq_vop_bandwidth_init);
783d0407baSopenharmony_ci
793d0407baSopenharmony_civoid rockchip_dmcfreq_vop_bandwidth_update(struct dmcfreq_vop_info *vop_info)
803d0407baSopenharmony_ci{
813d0407baSopenharmony_ci	unsigned long vop_last_rate, target = 0;
823d0407baSopenharmony_ci	unsigned int readlatency = 0;
833d0407baSopenharmony_ci	int i;
843d0407baSopenharmony_ci
853d0407baSopenharmony_ci	if (!common_info)
863d0407baSopenharmony_ci		return;
873d0407baSopenharmony_ci
883d0407baSopenharmony_ci	dev_dbg(common_info->dev, "line bw=%u, frame bw=%u, pn=%u\n",
893d0407baSopenharmony_ci		vop_info->line_bw_mbyte, vop_info->frame_bw_mbyte,
903d0407baSopenharmony_ci		vop_info->plane_num);
913d0407baSopenharmony_ci
923d0407baSopenharmony_ci	if (!common_info->vop_pn_rl_tbl || !common_info->set_msch_readlatency)
933d0407baSopenharmony_ci		goto vop_bw_tbl;
943d0407baSopenharmony_ci	for (i = 0; common_info->vop_pn_rl_tbl[i].rl != DMCFREQ_TABLE_END; i++) {
953d0407baSopenharmony_ci		if (vop_info->plane_num >= common_info->vop_pn_rl_tbl[i].pn)
963d0407baSopenharmony_ci			readlatency = common_info->vop_pn_rl_tbl[i].rl;
973d0407baSopenharmony_ci	}
983d0407baSopenharmony_ci	dev_dbg(common_info->dev, "pn=%u\n", vop_info->plane_num);
993d0407baSopenharmony_ci	if (readlatency) {
1003d0407baSopenharmony_ci		cancel_delayed_work_sync(&common_info->msch_rl_work);
1013d0407baSopenharmony_ci		common_info->is_msch_rl_work_started = false;
1023d0407baSopenharmony_ci		if (common_info->read_latency != readlatency)
1033d0407baSopenharmony_ci			set_msch_rl(readlatency);
1043d0407baSopenharmony_ci	} else if (common_info->read_latency &&
1053d0407baSopenharmony_ci		   !common_info->is_msch_rl_work_started) {
1063d0407baSopenharmony_ci		common_info->is_msch_rl_work_started = true;
1073d0407baSopenharmony_ci		schedule_delayed_work(&common_info->msch_rl_work,
1083d0407baSopenharmony_ci				      msecs_to_jiffies(MSCH_RL_DELAY_TIME));
1093d0407baSopenharmony_ci	}
1103d0407baSopenharmony_ci
1113d0407baSopenharmony_civop_bw_tbl:
1123d0407baSopenharmony_ci	if (!common_info->auto_freq_en || !common_info->vop_bw_tbl)
1133d0407baSopenharmony_ci		goto vop_frame_bw_tbl;
1143d0407baSopenharmony_ci
1153d0407baSopenharmony_ci	for (i = 0; common_info->vop_bw_tbl[i].freq != DMCFREQ_TABLE_END; i++) {
1163d0407baSopenharmony_ci		if (vop_info->line_bw_mbyte >= common_info->vop_bw_tbl[i].min)
1173d0407baSopenharmony_ci			target = common_info->vop_bw_tbl[i].freq;
1183d0407baSopenharmony_ci	}
1193d0407baSopenharmony_ci
1203d0407baSopenharmony_civop_frame_bw_tbl:
1213d0407baSopenharmony_ci	if (!common_info->auto_freq_en || !common_info->vop_frame_bw_tbl)
1223d0407baSopenharmony_ci		goto next;
1233d0407baSopenharmony_ci	for (i = 0; common_info->vop_frame_bw_tbl[i].freq != DMCFREQ_TABLE_END;
1243d0407baSopenharmony_ci	     i++) {
1253d0407baSopenharmony_ci		if (vop_info->frame_bw_mbyte >= common_info->vop_frame_bw_tbl[i].min) {
1263d0407baSopenharmony_ci			if (target < common_info->vop_frame_bw_tbl[i].freq)
1273d0407baSopenharmony_ci				target = common_info->vop_frame_bw_tbl[i].freq;
1283d0407baSopenharmony_ci		}
1293d0407baSopenharmony_ci	}
1303d0407baSopenharmony_ci
1313d0407baSopenharmony_cinext:
1323d0407baSopenharmony_ci	vop_last_rate = common_info->vop_req_rate;
1333d0407baSopenharmony_ci	common_info->vop_req_rate = target;
1343d0407baSopenharmony_ci
1353d0407baSopenharmony_ci	if (target > vop_last_rate) {
1363d0407baSopenharmony_ci		mutex_lock(&common_info->devfreq->lock);
1373d0407baSopenharmony_ci		update_devfreq(common_info->devfreq);
1383d0407baSopenharmony_ci		mutex_unlock(&common_info->devfreq->lock);
1393d0407baSopenharmony_ci	}
1403d0407baSopenharmony_ci}
1413d0407baSopenharmony_ciEXPORT_SYMBOL(rockchip_dmcfreq_vop_bandwidth_update);
1423d0407baSopenharmony_ci
1433d0407baSopenharmony_ciint rockchip_dmcfreq_vop_bandwidth_request(struct dmcfreq_vop_info *vop_info)
1443d0407baSopenharmony_ci{
1453d0407baSopenharmony_ci	unsigned long target = 0;
1463d0407baSopenharmony_ci	int i;
1473d0407baSopenharmony_ci
1483d0407baSopenharmony_ci	if (!common_info || !common_info->auto_freq_en ||
1493d0407baSopenharmony_ci	    !common_info->vop_bw_tbl)
1503d0407baSopenharmony_ci		return 0;
1513d0407baSopenharmony_ci
1523d0407baSopenharmony_ci	for (i = 0; common_info->vop_bw_tbl[i].freq != DMCFREQ_TABLE_END; i++) {
1533d0407baSopenharmony_ci		if (vop_info->line_bw_mbyte <= common_info->vop_bw_tbl[i].max) {
1543d0407baSopenharmony_ci			target = common_info->vop_bw_tbl[i].freq;
1553d0407baSopenharmony_ci			break;
1563d0407baSopenharmony_ci		}
1573d0407baSopenharmony_ci	}
1583d0407baSopenharmony_ci
1593d0407baSopenharmony_ci	if (!target)
1603d0407baSopenharmony_ci		return -EINVAL;
1613d0407baSopenharmony_ci
1623d0407baSopenharmony_ci	return 0;
1633d0407baSopenharmony_ci}
1643d0407baSopenharmony_ciEXPORT_SYMBOL(rockchip_dmcfreq_vop_bandwidth_request);
1653d0407baSopenharmony_ci
1663d0407baSopenharmony_ciMODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>");
1673d0407baSopenharmony_ciMODULE_DESCRIPTION("rockchip dmcfreq driver with devfreq framework");
1683d0407baSopenharmony_ciMODULE_LICENSE("GPL v2");
169