1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3 * Rockchip dmc common functions.
4 *
5 * Copyright (c) 2022 Rockchip Electronics Co. Ltd.
6 * Author: Finley Xiao <finley.xiao@rock-chips.com>
7 */
8
9 #include <linux/module.h>
10 #include <soc/rockchip/rockchip_dmc.h>
11
12 #define msch_rl_to_dmcfreq(work) container_of(to_delayed_work(work), struct rockchip_dmcfreq, msch_rl_work)
13 #define MSCH_RL_DELAY_TIME 50 /* ms */
14
15 static struct dmcfreq_common_info *common_info;
16 static DECLARE_RWSEM(rockchip_dmcfreq_sem);
17
rockchip_dmcfreq_lock(void)18 void rockchip_dmcfreq_lock(void)
19 {
20 down_read(&rockchip_dmcfreq_sem);
21 }
22 EXPORT_SYMBOL(rockchip_dmcfreq_lock);
23
rockchip_dmcfreq_lock_nested(void)24 void rockchip_dmcfreq_lock_nested(void)
25 {
26 down_read_nested(&rockchip_dmcfreq_sem, SINGLE_DEPTH_NESTING);
27 }
28 EXPORT_SYMBOL(rockchip_dmcfreq_lock_nested);
29
rockchip_dmcfreq_unlock(void)30 void rockchip_dmcfreq_unlock(void)
31 {
32 up_read(&rockchip_dmcfreq_sem);
33 }
34 EXPORT_SYMBOL(rockchip_dmcfreq_unlock);
35
rockchip_dmcfreq_write_trylock(void)36 int rockchip_dmcfreq_write_trylock(void)
37 {
38 return down_write_trylock(&rockchip_dmcfreq_sem);
39 }
40 EXPORT_SYMBOL(rockchip_dmcfreq_write_trylock);
41
rockchip_dmcfreq_write_unlock(void)42 void rockchip_dmcfreq_write_unlock(void)
43 {
44 up_write(&rockchip_dmcfreq_sem);
45 }
46 EXPORT_SYMBOL(rockchip_dmcfreq_write_unlock);
47
set_msch_rl(unsigned int readlatency)48 static void set_msch_rl(unsigned int readlatency)
49
50 {
51 rockchip_dmcfreq_lock();
52 dev_dbg(common_info->dev, "rl 0x%x -> 0x%x\n", common_info->read_latency, readlatency);
53 if (!common_info->set_msch_readlatency(readlatency)) {
54 common_info->read_latency = readlatency;
55 } else {
56 dev_err(common_info->dev, "failed to set msch rl\n");
57 }
58 rockchip_dmcfreq_unlock();
59 }
60
set_msch_rl_work(struct work_struct *work)61 static void set_msch_rl_work(struct work_struct *work)
62 {
63 set_msch_rl(0);
64 common_info->is_msch_rl_work_started = false;
65 }
66
rockchip_dmcfreq_vop_bandwidth_init(struct dmcfreq_common_info *info)67 int rockchip_dmcfreq_vop_bandwidth_init(struct dmcfreq_common_info *info)
68 {
69 if (info->set_msch_readlatency) {
70 INIT_DELAYED_WORK(&info->msch_rl_work, set_msch_rl_work);
71 }
72 common_info = info;
73
74 return 0;
75 }
76 EXPORT_SYMBOL(rockchip_dmcfreq_vop_bandwidth_init);
77
rockchip_dmcfreq_vop_bandwidth_update(struct dmcfreq_vop_info *vop_info)78 void rockchip_dmcfreq_vop_bandwidth_update(struct dmcfreq_vop_info *vop_info)
79 {
80 unsigned long vop_last_rate, target = 0;
81 unsigned int readlatency = 0;
82 int i;
83
84 if (!common_info) {
85 return;
86 }
87
88 dev_dbg(common_info->dev, "line bw=%u, frame bw=%u, pn=%u\n", vop_info->line_bw_mbyte, vop_info->frame_bw_mbyte,
89 vop_info->plane_num);
90
91 if (!common_info->vop_pn_rl_tbl || !common_info->set_msch_readlatency) {
92 goto vop_bw_tbl;
93 }
94 for (i = 0; common_info->vop_pn_rl_tbl[i].rl != DMCFREQ_TABLE_END; i++) {
95 if (vop_info->plane_num >= common_info->vop_pn_rl_tbl[i].pn) {
96 readlatency = common_info->vop_pn_rl_tbl[i].rl;
97 }
98 }
99 dev_dbg(common_info->dev, "pn=%u\n", vop_info->plane_num);
100 if (readlatency) {
101 cancel_delayed_work_sync(&common_info->msch_rl_work);
102 common_info->is_msch_rl_work_started = false;
103 if (common_info->read_latency != readlatency) {
104 set_msch_rl(readlatency);
105 }
106 } else if (common_info->read_latency && !common_info->is_msch_rl_work_started) {
107 common_info->is_msch_rl_work_started = true;
108 schedule_delayed_work(&common_info->msch_rl_work, msecs_to_jiffies(MSCH_RL_DELAY_TIME));
109 }
110
111 vop_bw_tbl:
112 if (!common_info->auto_freq_en || !common_info->vop_bw_tbl) {
113 goto vop_frame_bw_tbl;
114 }
115
116 for (i = 0; common_info->vop_bw_tbl[i].freq != DMCFREQ_TABLE_END; i++) {
117 if (vop_info->line_bw_mbyte >= common_info->vop_bw_tbl[i].min) {
118 target = common_info->vop_bw_tbl[i].freq;
119 }
120 }
121
122 vop_frame_bw_tbl:
123 if (!common_info->auto_freq_en || !common_info->vop_frame_bw_tbl) {
124 goto next;
125 }
126 for (i = 0; common_info->vop_frame_bw_tbl[i].freq != DMCFREQ_TABLE_END; i++) {
127 if (vop_info->frame_bw_mbyte >= common_info->vop_frame_bw_tbl[i].min) {
128 if (target < common_info->vop_frame_bw_tbl[i].freq) {
129 target = common_info->vop_frame_bw_tbl[i].freq;
130 }
131 }
132 }
133
134 next:
135 vop_last_rate = common_info->vop_req_rate;
136 common_info->vop_req_rate = target;
137
138 if (target > vop_last_rate) {
139 mutex_lock(&common_info->devfreq->lock);
140 update_devfreq(common_info->devfreq);
141 mutex_unlock(&common_info->devfreq->lock);
142 }
143 }
144 EXPORT_SYMBOL(rockchip_dmcfreq_vop_bandwidth_update);
145
rockchip_dmcfreq_vop_bandwidth_request(struct dmcfreq_vop_info *vop_info)146 int rockchip_dmcfreq_vop_bandwidth_request(struct dmcfreq_vop_info *vop_info)
147 {
148 unsigned long target = 0;
149 int i;
150
151 if (!common_info || !common_info->auto_freq_en || !common_info->vop_bw_tbl) {
152 return 0;
153 }
154
155 for (i = 0; common_info->vop_bw_tbl[i].freq != DMCFREQ_TABLE_END; i++) {
156 if (vop_info->line_bw_mbyte <= common_info->vop_bw_tbl[i].max) {
157 target = common_info->vop_bw_tbl[i].freq;
158 break;
159 }
160 }
161
162 if (!target) {
163 return -EINVAL;
164 }
165
166 return 0;
167 }
168 EXPORT_SYMBOL(rockchip_dmcfreq_vop_bandwidth_request);
169
170 MODULE_AUTHOR("Finley Xiao <finley.xiao@rock-chips.com>");
171 MODULE_DESCRIPTION("rockchip dmcfreq driver with devfreq framework");
172 MODULE_LICENSE("GPL v2");
173