hc
2024-08-19 a51341d8c7882adfad4f167bc7c3ca616908b53d
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// 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, pn_4k=%u\n",
       vop_info->line_bw_mbyte, vop_info->frame_bw_mbyte,
       vop_info->plane_num, vop_info->plane_num_4k);
 
   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:
   if (vop_info->plane_num_4k && target < common_info->vop_4k_rate)
       target = common_info->vop_4k_rate;
 
   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");