Orange Pi5 kernel

Deprecated Linux kernel 5.10.110 for OrangePi 5/5B/5+ boards

3 Commits   0 Branches   0 Tags
// SPDX-License-Identifier: GPL-2.0-only
/*
 * Rockchip dmc common functions.
 *
 * Copyright (c) 2021 Rockchip Electronics Co. Ltd.
 * Author: Finley Xiao <finley.xiao@rock-chips.com>
 */

#include <linux/module.h>
#include <soc/rockchip/rockchip_dmc.h>

#define msch_rl_to_dmcfreq(work) container_of(to_delayed_work(work), \
					      struct rockchip_dmcfreq, \
					      msch_rl_work)
#define MSCH_RL_DELAY_TIME	50 /* ms */

static struct dmcfreq_common_info *common_info;
static DECLARE_RWSEM(rockchip_dmcfreq_sem);

void rockchip_dmcfreq_lock(void)
{
	down_read(&rockchip_dmcfreq_sem);
}
EXPORT_SYMBOL(rockchip_dmcfreq_lock);

void rockchip_dmcfreq_lock_nested(void)
{
	down_read_nested(&rockchip_dmcfreq_sem, SINGLE_DEPTH_NESTING);
}
EXPORT_SYMBOL(rockchip_dmcfreq_lock_nested);

void rockchip_dmcfreq_unlock(void)
{
	up_read(&rockchip_dmcfreq_sem);
}
EXPORT_SYMBOL(rockchip_dmcfreq_unlock);

int rockchip_dmcfreq_write_trylock(void)
{
	return down_write_trylock(&rockchip_dmcfreq_sem);
}
EXPORT_SYMBOL(rockchip_dmcfreq_write_trylock);

void rockchip_dmcfreq_write_unlock(void)
{
	up_write(&rockchip_dmcfreq_sem);
}
EXPORT_SYMBOL(rockchip_dmcfreq_write_unlock);

static void set_msch_rl(unsigned int readlatency)

{
	rockchip_dmcfreq_lock();
	dev_dbg(common_info->dev, "rl 0x%x -> 0x%x\n",
		common_info->read_latency, readlatency);
	if (!common_info->set_msch_readlatency(readlatency))
		common_info->read_latency = readlatency;
	else
		dev_err(common_info->dev, "failed to set msch rl\n");
	rockchip_dmcfreq_unlock();
}

static void set_msch_rl_work(struct work_struct *work)
{
	set_msch_rl(0);
	common_info->is_msch_rl_work_started = false;
}

int rockchip_dmcfreq_vop_bandwidth_init(struct dmcfreq_common_info *info)
{
	if (info->set_msch_readlatency)
		INIT_DELAYED_WORK(&info->msch_rl_work, set_msch_rl_work);
	common_info = info;

	return 0;
}
EXPORT_SYMBOL(rockchip_dmcfreq_vop_bandwidth_init);

void rockchip_dmcfreq_vop_bandwidth_update(struct dmcfreq_vop_info *vop_info)
{
	unsigned long vop_last_rate, target = 0;
	unsigned int readlatency = 0;
	int i;

	if (!common_info)
		return;

	dev_dbg(common_info->dev, "line bw=%u, frame bw=%u, pn=%u\n",
		vop_info->line_bw_mbyte, vop_info->frame_bw_mbyte,
		vop_info->plane_num);

	if (!common_info->vop_pn_rl_tbl || !common_info->set_msch_readlatency)
		goto vop_bw_tbl;
	for (i = 0; common_info->vop_pn_rl_tbl[i].rl != DMCFREQ_TABLE_END; i++) {
		if (vop_info->plane_num >= common_info->vop_pn_rl_tbl[i].pn)
			readlatency = common_info->vop_pn_rl_tbl[i].rl;
	}
	dev_dbg(common_info->dev, "pn=%u\n", vop_info->plane_num);
	if (readlatency) {
		cancel_delayed_work_sync(&common_info->msch_rl_work);
		common_info->is_msch_rl_work_started = false;
		if (common_info->read_latency != readlatency)
			set_msch_rl(readlatency);
	} else if (common_info->read_latency &&
		   !common_info->is_msch_rl_work_started) {
		common_info->is_msch_rl_work_started = true;
		schedule_delayed_work(&common_info->msch_rl_work,
				      msecs_to_jiffies(MSCH_RL_DELAY_TIME));
	}

vop_bw_tbl:
	if (!common_info->auto_freq_en || !common_info->vop_bw_tbl)
		goto vop_frame_bw_tbl;

	for (i = 0; common_info->vop_bw_tbl[i].freq != DMCFREQ_TABLE_END; i++) {
		if (vop_info->line_bw_mbyte >= common_info->vop_bw_tbl[i].min)
			target = common_info->vop_bw_tbl[i].freq;
	}

vop_frame_bw_tbl:
	if (!common_info->auto_freq_en || !common_info->vop_frame_bw_tbl)
		goto next;
	for (i = 0; common_info->vop_frame_bw_tbl[i].freq != DMCFREQ_TABLE_END;
	     i++) {
		if (vop_info->frame_bw_mbyte >= common_info->vop_frame_bw_tbl[i].min) {
			if (target < common_info->vop_frame_bw_tbl[i].freq)
				target = common_info->vop_frame_bw_tbl[i].freq;
		}
	}

next:
	vop_last_rate = common_info->vop_req_rate;
	common_info->vop_req_rate = target;

	if (target > vop_last_rate) {
		mutex_lock(&common_info->devfreq->lock);
		update_devfreq(common_info->devfreq);
		mutex_unlock(&common_info->devfreq->lock);
	}
}
EXPORT_SYMBOL(rockchip_dmcfreq_vop_bandwidth_update);

int rockchip_dmcfreq_vop_bandwidth_request(struct dmcfreq_vop_info *vop_info)
{
	unsigned long target = 0;
	int i;

	if (!common_info || !common_info->auto_freq_en ||
	    !common_info->vop_bw_tbl)
		return 0;

	for (i = 0; common_info->vop_bw_tbl[i].freq != DMCFREQ_TABLE_END; i++) {
		if (vop_info->line_bw_mbyte <= common_info->vop_bw_tbl[i].max) {
			target = common_info->vop_bw_tbl[i].freq;
			break;
		}
	}

	if (!target)
		return -EINVAL;

	return 0;
}
EXPORT_SYMBOL(rockchip_dmcfreq_vop_bandwidth_request);

MODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>");
MODULE_DESCRIPTION("rockchip dmcfreq driver with devfreq framework");
MODULE_LICENSE("GPL v2");