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