1 | /* |
2 | * Copyright 2020 Advanced Micro Devices, Inc. |
3 | * |
4 | * Permission is hereby granted, free of charge, to any person obtaining a |
5 | * copy of this software and associated documentation files (the "Software"), |
6 | * to deal in the Software without restriction, including without limitation |
7 | * the rights to use, copy, modify, merge, publish, distribute, sublicense, |
8 | * and/or sell copies of the Software, and to permit persons to whom the |
9 | * Software is furnished to do so, subject to the following conditions: |
10 | * |
11 | * The above copyright notice and this permission notice shall be included in |
12 | * all copies or substantial portions of the Software. |
13 | * |
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL |
17 | * THE COPYRIGHT HOLDER(S) OR AUTHOR(S) BE LIABLE FOR ANY CLAIM, DAMAGES OR |
18 | * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, |
19 | * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR |
20 | * OTHER DEALINGS IN THE SOFTWARE. |
21 | * |
22 | * Authors: AMD |
23 | * |
24 | */ |
25 | |
26 | #include "reg_helper.h" |
27 | #include "core_types.h" |
28 | #include "dc_dmub_srv.h" |
29 | #include "dcn301_panel_cntl.h" |
30 | #include "atom.h" |
31 | |
32 | #define TO_DCN301_PANEL_CNTL(panel_cntl)\ |
33 | container_of(panel_cntl, struct dcn301_panel_cntl, base) |
34 | |
35 | #define CTX \ |
36 | dcn301_panel_cntl->base.ctx |
37 | |
38 | #define DC_LOGGER \ |
39 | dcn301_panel_cntl->base.ctx->logger |
40 | |
41 | #define REG(reg)\ |
42 | dcn301_panel_cntl->regs->reg |
43 | |
44 | #undef FN |
45 | #define FN(reg_name, field_name) \ |
46 | dcn301_panel_cntl->shift->field_name, dcn301_panel_cntl->mask->field_name |
47 | |
48 | static unsigned int dcn301_get_16_bit_backlight_from_pwm(struct panel_cntl *panel_cntl) |
49 | { |
50 | uint64_t current_backlight; |
51 | uint32_t round_result; |
52 | uint32_t bl_period, bl_int_count; |
53 | uint32_t bl_pwm, fractional_duty_cycle_en; |
54 | uint32_t bl_period_mask, bl_pwm_mask; |
55 | struct dcn301_panel_cntl *dcn301_panel_cntl = TO_DCN301_PANEL_CNTL(panel_cntl); |
56 | |
57 | REG_GET(BL_PWM_PERIOD_CNTL, BL_PWM_PERIOD, &bl_period); |
58 | REG_GET(BL_PWM_PERIOD_CNTL, BL_PWM_PERIOD_BITCNT, &bl_int_count); |
59 | |
60 | REG_GET(BL_PWM_CNTL, BL_ACTIVE_INT_FRAC_CNT, &bl_pwm); |
61 | REG_GET(BL_PWM_CNTL, BL_PWM_FRACTIONAL_EN, &fractional_duty_cycle_en); |
62 | |
63 | if (bl_int_count == 0) |
64 | bl_int_count = 16; |
65 | |
66 | bl_period_mask = (1 << bl_int_count) - 1; |
67 | bl_period &= bl_period_mask; |
68 | |
69 | bl_pwm_mask = bl_period_mask << (16 - bl_int_count); |
70 | |
71 | if (fractional_duty_cycle_en == 0) |
72 | bl_pwm &= bl_pwm_mask; |
73 | else |
74 | bl_pwm &= 0xFFFF; |
75 | |
76 | current_backlight = (uint64_t)bl_pwm << (1 + bl_int_count); |
77 | |
78 | if (bl_period == 0) |
79 | bl_period = 0xFFFF; |
80 | |
81 | current_backlight = div_u64(dividend: current_backlight, divisor: bl_period); |
82 | current_backlight = (current_backlight + 1) >> 1; |
83 | |
84 | current_backlight = (uint64_t)(current_backlight) * bl_period; |
85 | |
86 | round_result = (uint32_t)(current_backlight & 0xFFFFFFFF); |
87 | |
88 | round_result = (round_result >> (bl_int_count-1)) & 1; |
89 | |
90 | current_backlight >>= bl_int_count; |
91 | current_backlight += round_result; |
92 | |
93 | return (uint32_t)(current_backlight); |
94 | } |
95 | |
96 | static uint32_t dcn301_panel_cntl_hw_init(struct panel_cntl *panel_cntl) |
97 | { |
98 | struct dcn301_panel_cntl *dcn301_panel_cntl = TO_DCN301_PANEL_CNTL(panel_cntl); |
99 | uint32_t value; |
100 | uint32_t current_backlight; |
101 | |
102 | /* It must not be 0, so we have to restore them |
103 | * Bios bug w/a - period resets to zero, |
104 | * restoring to cache values which is always correct |
105 | */ |
106 | REG_GET(BL_PWM_CNTL, BL_ACTIVE_INT_FRAC_CNT, &value); |
107 | |
108 | if (value == 0 || value == 1) { |
109 | if (panel_cntl->stored_backlight_registers.BL_PWM_CNTL != 0) { |
110 | REG_WRITE(BL_PWM_CNTL, |
111 | panel_cntl->stored_backlight_registers.BL_PWM_CNTL); |
112 | REG_WRITE(BL_PWM_CNTL2, |
113 | panel_cntl->stored_backlight_registers.BL_PWM_CNTL2); |
114 | REG_WRITE(BL_PWM_PERIOD_CNTL, |
115 | panel_cntl->stored_backlight_registers.BL_PWM_PERIOD_CNTL); |
116 | REG_UPDATE(PWRSEQ_REF_DIV, |
117 | BL_PWM_REF_DIV, |
118 | panel_cntl->stored_backlight_registers.LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV); |
119 | } else { |
120 | /* TODO: Note: This should not really happen since VBIOS |
121 | * should have initialized PWM registers on boot. |
122 | */ |
123 | REG_WRITE(BL_PWM_CNTL, 0xC000FA00); |
124 | REG_WRITE(BL_PWM_PERIOD_CNTL, 0x000C0FA0); |
125 | } |
126 | } else { |
127 | panel_cntl->stored_backlight_registers.BL_PWM_CNTL = |
128 | REG_READ(BL_PWM_CNTL); |
129 | panel_cntl->stored_backlight_registers.BL_PWM_CNTL2 = |
130 | REG_READ(BL_PWM_CNTL2); |
131 | panel_cntl->stored_backlight_registers.BL_PWM_PERIOD_CNTL = |
132 | REG_READ(BL_PWM_PERIOD_CNTL); |
133 | |
134 | REG_GET(PWRSEQ_REF_DIV, BL_PWM_REF_DIV, |
135 | &panel_cntl->stored_backlight_registers.LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV); |
136 | } |
137 | |
138 | // Enable the backlight output |
139 | REG_UPDATE(BL_PWM_CNTL, BL_PWM_EN, 1); |
140 | |
141 | // Unlock group 2 backlight registers |
142 | REG_UPDATE(BL_PWM_GRP1_REG_LOCK, |
143 | BL_PWM_GRP1_REG_LOCK, 0); |
144 | |
145 | current_backlight = dcn301_get_16_bit_backlight_from_pwm(panel_cntl); |
146 | |
147 | return current_backlight; |
148 | } |
149 | |
150 | static void dcn301_panel_cntl_destroy(struct panel_cntl **panel_cntl) |
151 | { |
152 | struct dcn301_panel_cntl *dcn301_panel_cntl = TO_DCN301_PANEL_CNTL(*panel_cntl); |
153 | |
154 | kfree(objp: dcn301_panel_cntl); |
155 | *panel_cntl = NULL; |
156 | } |
157 | |
158 | static bool dcn301_is_panel_backlight_on(struct panel_cntl *panel_cntl) |
159 | { |
160 | struct dcn301_panel_cntl *dcn301_panel_cntl = TO_DCN301_PANEL_CNTL(panel_cntl); |
161 | uint32_t value; |
162 | |
163 | REG_GET(PWRSEQ_CNTL, PANEL_BLON, &value); |
164 | |
165 | return value; |
166 | } |
167 | |
168 | static bool dcn301_is_panel_powered_on(struct panel_cntl *panel_cntl) |
169 | { |
170 | struct dcn301_panel_cntl *dcn301_panel_cntl = TO_DCN301_PANEL_CNTL(panel_cntl); |
171 | uint32_t pwr_seq_state, dig_on, dig_on_ovrd; |
172 | |
173 | REG_GET(PWRSEQ_STATE, PANEL_PWRSEQ_TARGET_STATE_R, &pwr_seq_state); |
174 | |
175 | REG_GET_2(PWRSEQ_CNTL, PANEL_DIGON, &dig_on, PANEL_DIGON_OVRD, &dig_on_ovrd); |
176 | |
177 | return (pwr_seq_state == 1) || (dig_on == 1 && dig_on_ovrd == 1); |
178 | } |
179 | |
180 | static void dcn301_store_backlight_level(struct panel_cntl *panel_cntl) |
181 | { |
182 | struct dcn301_panel_cntl *dcn301_panel_cntl = TO_DCN301_PANEL_CNTL(panel_cntl); |
183 | |
184 | panel_cntl->stored_backlight_registers.BL_PWM_CNTL = |
185 | REG_READ(BL_PWM_CNTL); |
186 | panel_cntl->stored_backlight_registers.BL_PWM_CNTL2 = |
187 | REG_READ(BL_PWM_CNTL2); |
188 | panel_cntl->stored_backlight_registers.BL_PWM_PERIOD_CNTL = |
189 | REG_READ(BL_PWM_PERIOD_CNTL); |
190 | |
191 | REG_GET(PWRSEQ_REF_DIV, BL_PWM_REF_DIV, |
192 | &panel_cntl->stored_backlight_registers.LVTMA_PWRSEQ_REF_DIV_BL_PWM_REF_DIV); |
193 | } |
194 | |
195 | static const struct panel_cntl_funcs dcn301_link_panel_cntl_funcs = { |
196 | .destroy = dcn301_panel_cntl_destroy, |
197 | .hw_init = dcn301_panel_cntl_hw_init, |
198 | .is_panel_backlight_on = dcn301_is_panel_backlight_on, |
199 | .is_panel_powered_on = dcn301_is_panel_powered_on, |
200 | .store_backlight_level = dcn301_store_backlight_level, |
201 | .get_current_backlight = dcn301_get_16_bit_backlight_from_pwm, |
202 | }; |
203 | |
204 | void dcn301_panel_cntl_construct( |
205 | struct dcn301_panel_cntl *dcn301_panel_cntl, |
206 | const struct panel_cntl_init_data *init_data, |
207 | const struct dce_panel_cntl_registers *regs, |
208 | const struct dcn301_panel_cntl_shift *shift, |
209 | const struct dcn301_panel_cntl_mask *mask) |
210 | { |
211 | dcn301_panel_cntl->regs = regs; |
212 | dcn301_panel_cntl->shift = shift; |
213 | dcn301_panel_cntl->mask = mask; |
214 | |
215 | dcn301_panel_cntl->base.funcs = &dcn301_link_panel_cntl_funcs; |
216 | dcn301_panel_cntl->base.ctx = init_data->ctx; |
217 | dcn301_panel_cntl->base.inst = init_data->inst; |
218 | dcn301_panel_cntl->base.pwrseq_inst = 0; |
219 | } |
220 | |