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