]>
Commit | Line | Data |
---|---|---|
03acaa9b JP |
1 | From 8149051c34bc3d4c55adc56d04ffb7f7a04c2fd9 Mon Sep 17 00:00:00 2001 |
2 | From: Dan Johansen <strit@manjaro.org> | |
3 | Date: Sun, 2 Jan 2022 16:45:28 +0100 | |
4 | Subject: [PATCH 1/2] Add megis extcon changes to fusb302 | |
8c9054af | 5 | |
03acaa9b | 6 | Signed-off-by: Dan Johansen <strit@manjaro.org> |
8c9054af | 7 | --- |
03acaa9b JP |
8 | drivers/phy/rockchip/phy-rockchip-typec.c | 5 +++ |
9 | drivers/usb/typec/Kconfig | 7 ++++ | |
10 | drivers/usb/typec/Makefile | 1 + | |
11 | drivers/usb/typec/tcpm/fusb302.c | 47 ++++++++++++++++------- | |
12 | drivers/usb/typec/tcpm/fusb302_reg.h | 16 ++++---- | |
13 | 5 files changed, 53 insertions(+), 23 deletions(-) | |
8c9054af | 14 | |
03acaa9b JP |
15 | diff --git a/drivers/phy/rockchip/phy-rockchip-typec.c b/drivers/phy/rockchip/phy-rockchip-typec.c |
16 | index d2bbdc96a167..fa10ee9a5794 100644 | |
17 | --- a/drivers/phy/rockchip/phy-rockchip-typec.c | |
18 | +++ b/drivers/phy/rockchip/phy-rockchip-typec.c | |
19 | @@ -350,6 +350,7 @@ struct usb3phy_reg { | |
20 | * struct rockchip_usb3phy_port_cfg - usb3-phy port configuration. | |
21 | * @reg: the base address for usb3-phy config. | |
22 | * @typec_conn_dir: the register of type-c connector direction. | |
23 | + * @typec_conn_dir_sel: the register of type-c connector direction source. | |
24 | * @usb3tousb2_en: the register of type-c force usb2 to usb2 enable. | |
25 | * @external_psm: the register of type-c phy external psm clock. | |
26 | * @pipe_status: the register of type-c phy pipe status. | |
27 | @@ -360,6 +361,7 @@ struct usb3phy_reg { | |
28 | struct rockchip_usb3phy_port_cfg { | |
29 | unsigned int reg; | |
30 | struct usb3phy_reg typec_conn_dir; | |
31 | + struct usb3phy_reg typec_conn_dir_sel; | |
32 | struct usb3phy_reg usb3tousb2_en; | |
33 | struct usb3phy_reg external_psm; | |
34 | struct usb3phy_reg pipe_status; | |
35 | @@ -434,6 +436,7 @@ static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { | |
36 | { | |
37 | .reg = 0xff7c0000, | |
38 | .typec_conn_dir = { 0xe580, 0, 16 }, | |
39 | + .typec_conn_dir_sel = { 0xe580, 8, 16+8 }, | |
40 | .usb3tousb2_en = { 0xe580, 3, 19 }, | |
41 | .external_psm = { 0xe588, 14, 30 }, | |
42 | .pipe_status = { 0xe5c0, 0, 0 }, | |
43 | @@ -444,6 +447,7 @@ static const struct rockchip_usb3phy_port_cfg rk3399_usb3phy_port_cfgs[] = { | |
44 | { | |
45 | .reg = 0xff800000, | |
46 | .typec_conn_dir = { 0xe58c, 0, 16 }, | |
47 | + .typec_conn_dir_sel = { 0xe58c, 8, 16+8 }, | |
48 | .usb3tousb2_en = { 0xe58c, 3, 19 }, | |
49 | .external_psm = { 0xe594, 14, 30 }, | |
50 | .pipe_status = { 0xe5c0, 16, 16 }, | |
51 | @@ -739,6 +743,7 @@ static int tcphy_phy_init(struct rockchip_typec_phy *tcphy, u8 mode) | |
8c9054af | 52 | |
03acaa9b | 53 | reset_control_deassert(tcphy->tcphy_rst); |
8c9054af | 54 | |
03acaa9b JP |
55 | + property_enable(tcphy, &cfg->typec_conn_dir_sel, 0); |
56 | property_enable(tcphy, &cfg->typec_conn_dir, tcphy->flip); | |
57 | tcphy_dp_aux_set_flip(tcphy); | |
8c9054af | 58 | |
03acaa9b JP |
59 | diff --git a/drivers/usb/typec/Kconfig b/drivers/usb/typec/Kconfig |
60 | index ab480f38523a..01ecc5e590f1 100644 | |
61 | --- a/drivers/usb/typec/Kconfig | |
62 | +++ b/drivers/usb/typec/Kconfig | |
63 | @@ -88,6 +88,13 @@ config TYPEC_QCOM_PMIC | |
64 | It will also enable the VBUS output to connected devices when a | |
65 | DFP connection is made. | |
66 | ||
67 | +config TYPEC_EXTCON | |
68 | + tristate "Type-C switch/mux -> extcon interface bridge driver" | |
69 | + depends on USB_ROLE_SWITCH | |
70 | + help | |
71 | + Say Y or M here if your system needs bridging between typec class | |
72 | + and extcon interfaces. | |
8c9054af | 73 | + |
03acaa9b JP |
74 | source "drivers/usb/typec/mux/Kconfig" |
75 | ||
76 | source "drivers/usb/typec/altmodes/Kconfig" | |
77 | diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile | |
78 | index a0adb8947a30..d9d829386b73 100644 | |
79 | --- a/drivers/usb/typec/Makefile | |
80 | +++ b/drivers/usb/typec/Makefile | |
81 | @@ -8,4 +8,5 @@ obj-$(CONFIG_TYPEC_TPS6598X) += tipd/ | |
82 | obj-$(CONFIG_TYPEC_HD3SS3220) += hd3ss3220.o | |
83 | obj-$(CONFIG_TYPEC_QCOM_PMIC) += qcom-pmic-typec.o | |
84 | obj-$(CONFIG_TYPEC_STUSB160X) += stusb160x.o | |
85 | +obj-$(CONFIG_TYPEC_EXTCON) += typec-extcon.o | |
86 | obj-$(CONFIG_TYPEC) += mux/ | |
87 | diff --git a/drivers/usb/typec/tcpm/fusb302.c b/drivers/usb/typec/tcpm/fusb302.c | |
88 | index 72f9001b0792..cb26793f90f8 100644 | |
89 | --- a/drivers/usb/typec/tcpm/fusb302.c | |
90 | +++ b/drivers/usb/typec/tcpm/fusb302.c | |
91 | @@ -440,6 +440,16 @@ static int tcpm_get_current_limit(struct tcpc_dev *dev) | |
92 | int current_limit = 0; | |
93 | unsigned long timeout; | |
94 | ||
95 | + /* | |
96 | + * To avoid cycles in OF dependencies, we get extcon when necessary | |
97 | + * outside of probe function. | |
98 | + */ | |
99 | + if (of_property_read_bool(chip->dev->of_node, "extcon") && !chip->extcon) { | |
100 | + chip->extcon = extcon_get_edev_by_phandle(chip->dev, 0); | |
101 | + if (IS_ERR(chip->extcon)) | |
102 | + chip->extcon = NULL; | |
8c9054af JP |
103 | + } |
104 | + | |
03acaa9b JP |
105 | if (!chip->extcon) |
106 | return 0; | |
107 | ||
108 | @@ -498,6 +508,7 @@ static int fusb302_set_toggling(struct fusb302_chip *chip, | |
109 | enum toggling_mode mode) | |
110 | { | |
111 | int ret = 0; | |
112 | + u8 reg; | |
113 | ||
114 | /* first disable toggling */ | |
115 | ret = fusb302_i2c_clear_bits(chip, FUSB_REG_CONTROL2, | |
116 | @@ -556,6 +567,12 @@ static int fusb302_set_toggling(struct fusb302_chip *chip, | |
117 | } else { | |
118 | /* Datasheet says vconn MUST be off when toggling */ | |
119 | WARN(chip->vconn_on, "Vconn is on during toggle start"); | |
8c9054af | 120 | + |
03acaa9b JP |
121 | + /* clear interrupts */ |
122 | + ret = fusb302_i2c_read(chip, FUSB_REG_INTERRUPT, ®); | |
123 | + if (ret < 0) | |
124 | + return ret; | |
8c9054af | 125 | + |
03acaa9b JP |
126 | /* unmask TOGDONE interrupt */ |
127 | ret = fusb302_i2c_clear_bits(chip, FUSB_REG_MASKA, | |
128 | FUSB_REG_MASKA_TOGDONE); | |
129 | @@ -635,6 +652,14 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc) | |
130 | goto done; | |
131 | } | |
132 | ||
133 | + /* adjust current for SRC */ | |
134 | + ret = fusb302_set_src_current(chip, cc_src_current[cc]); | |
135 | + if (ret < 0) { | |
136 | + fusb302_log(chip, "cannot set src current %s, ret=%d", | |
137 | + typec_cc_status_name[cc], ret); | |
138 | + goto done; | |
8c9054af | 139 | + } |
8c9054af | 140 | + |
03acaa9b JP |
141 | ret = fusb302_i2c_mask_write(chip, FUSB_REG_SWITCHES0, |
142 | switches0_mask, switches0_data); | |
143 | if (ret < 0) { | |
144 | @@ -645,14 +670,6 @@ static int tcpm_set_cc(struct tcpc_dev *dev, enum typec_cc_status cc) | |
145 | chip->cc1 = TYPEC_CC_OPEN; | |
146 | chip->cc2 = TYPEC_CC_OPEN; | |
8c9054af | 147 | |
03acaa9b JP |
148 | - /* adjust current for SRC */ |
149 | - ret = fusb302_set_src_current(chip, cc_src_current[cc]); | |
150 | - if (ret < 0) { | |
151 | - fusb302_log(chip, "cannot set src current %s, ret=%d", | |
152 | - typec_cc_status_name[cc], ret); | |
153 | - goto done; | |
8c9054af | 154 | - } |
03acaa9b JP |
155 | - |
156 | /* enable/disable interrupts, BC_LVL for SNK and COMP_CHNG for SRC */ | |
157 | switch (cc) { | |
158 | case TYPEC_CC_RP_DEF: | |
159 | @@ -1528,14 +1545,16 @@ static void fusb302_irq_work(struct work_struct *work) | |
160 | "IRQ: 0x%02x, a: 0x%02x, b: 0x%02x, status0: 0x%02x", | |
161 | interrupt, interrupta, interruptb, status0); | |
8c9054af | 162 | |
03acaa9b JP |
163 | - if (interrupt & FUSB_REG_INTERRUPT_VBUSOK) { |
164 | - vbus_present = !!(status0 & FUSB_REG_STATUS0_VBUSOK); | |
165 | + vbus_present = !!(status0 & FUSB_REG_STATUS0_VBUSOK); | |
166 | + if (interrupt & FUSB_REG_INTERRUPT_VBUSOK) | |
167 | fusb302_log(chip, "IRQ: VBUS_OK, vbus=%s", | |
168 | vbus_present ? "On" : "Off"); | |
169 | - if (vbus_present != chip->vbus_present) { | |
170 | - chip->vbus_present = vbus_present; | |
171 | - tcpm_vbus_change(chip->tcpm_port); | |
8c9054af | 172 | - } |
03acaa9b JP |
173 | + if (vbus_present != chip->vbus_present) { |
174 | + chip->vbus_present = vbus_present; | |
175 | + if (!(interrupt & FUSB_REG_INTERRUPT_VBUSOK)) | |
176 | + fusb302_log(chip, "IRQ: VBUS changed without interrupt, vbus=%s", | |
177 | + vbus_present ? "On" : "Off"); | |
178 | + tcpm_vbus_change(chip->tcpm_port); | |
8c9054af | 179 | } |
8c9054af | 180 | |
03acaa9b JP |
181 | if ((interrupta & FUSB_REG_INTERRUPTA_TOGDONE) && intr_togdone) { |
182 | diff --git a/drivers/usb/typec/tcpm/fusb302_reg.h b/drivers/usb/typec/tcpm/fusb302_reg.h | |
183 | index edc0e4b0f1e6..f37d226c5027 100644 | |
184 | --- a/drivers/usb/typec/tcpm/fusb302_reg.h | |
185 | +++ b/drivers/usb/typec/tcpm/fusb302_reg.h | |
186 | @@ -27,14 +27,13 @@ | |
187 | #define FUSB_REG_SWITCHES1_TXCC2_EN BIT(1) | |
188 | #define FUSB_REG_SWITCHES1_TXCC1_EN BIT(0) | |
189 | #define FUSB_REG_MEASURE 0x04 | |
190 | -#define FUSB_REG_MEASURE_MDAC5 BIT(7) | |
191 | -#define FUSB_REG_MEASURE_MDAC4 BIT(6) | |
192 | -#define FUSB_REG_MEASURE_MDAC3 BIT(5) | |
193 | -#define FUSB_REG_MEASURE_MDAC2 BIT(4) | |
194 | -#define FUSB_REG_MEASURE_MDAC1 BIT(3) | |
195 | -#define FUSB_REG_MEASURE_MDAC0 BIT(2) | |
196 | -#define FUSB_REG_MEASURE_VBUS BIT(1) | |
197 | -#define FUSB_REG_MEASURE_XXXX5 BIT(0) | |
198 | +#define FUSB_REG_MEASURE_VBUS BIT(6) | |
199 | +#define FUSB_REG_MEASURE_MDAC5 BIT(5) | |
200 | +#define FUSB_REG_MEASURE_MDAC4 BIT(4) | |
201 | +#define FUSB_REG_MEASURE_MDAC3 BIT(3) | |
202 | +#define FUSB_REG_MEASURE_MDAC2 BIT(2) | |
203 | +#define FUSB_REG_MEASURE_MDAC1 BIT(1) | |
204 | +#define FUSB_REG_MEASURE_MDAC0 BIT(0) | |
205 | #define FUSB_REG_CONTROL0 0x06 | |
206 | #define FUSB_REG_CONTROL0_TX_FLUSH BIT(6) | |
207 | #define FUSB_REG_CONTROL0_INT_MASK BIT(5) | |
208 | @@ -105,7 +104,6 @@ | |
209 | #define FUSB_REG_STATUS0A_RX_SOFT_RESET BIT(1) | |
210 | #define FUSB_REG_STATUS0A_RX_HARD_RESET BIT(0) | |
211 | #define FUSB_REG_STATUS1A 0x3D | |
212 | -#define FUSB_REG_STATUS1A_TOGSS BIT(3) | |
213 | #define FUSB_REG_STATUS1A_TOGSS_RUNNING 0x0 | |
214 | #define FUSB_REG_STATUS1A_TOGSS_SRC1 0x1 | |
215 | #define FUSB_REG_STATUS1A_TOGSS_SRC2 0x2 | |
8c9054af | 216 | -- |
03acaa9b | 217 | 2.34.1 |
8c9054af | 218 | |
03acaa9b JP |
219 | From 6af2e6a2d59bd755234e5e15a47dfa669788143c Mon Sep 17 00:00:00 2001 |
220 | From: Dan Johansen <strit@manjaro.org> | |
221 | Date: Sun, 2 Jan 2022 16:47:40 +0100 | |
222 | Subject: [PATCH 2/2] usb: typec: Add megis typex to extcon bridge driver | |
8c9054af | 223 | |
03acaa9b | 224 | Signed-off-by: Dan Johansen <strit@manjaro.org> |
8c9054af | 225 | --- |
03acaa9b JP |
226 | drivers/usb/typec/typec-extcon.c | 337 +++++++++++++++++++++++++++++++ |
227 | 1 file changed, 337 insertions(+) | |
228 | create mode 100644 drivers/usb/typec/typec-extcon.c | |
8c9054af | 229 | |
03acaa9b JP |
230 | diff --git a/drivers/usb/typec/typec-extcon.c b/drivers/usb/typec/typec-extcon.c |
231 | new file mode 100644 | |
232 | index 000000000000..143ff2486f2f | |
233 | --- /dev/null | |
234 | +++ b/drivers/usb/typec/typec-extcon.c | |
235 | @@ -0,0 +1,337 @@ | |
236 | +/* | |
237 | + * typec -> extcon bridge | |
238 | + * Copyright (c) 2021 Ondřej Jirman <megi@xff.cz> | |
239 | + * | |
240 | + * This driver bridges standard type-c interfaces to drivers that | |
241 | + * expect extcon interface. | |
242 | + */ | |
243 | + | |
244 | +#include <linux/delay.h> | |
245 | +#include <linux/kernel.h> | |
246 | +#include <linux/module.h> | |
247 | +#include <linux/power_supply.h> | |
248 | +#include <linux/platform_device.h> | |
249 | +#include <linux/usb/pd.h> | |
250 | +#include <linux/usb/role.h> | |
251 | +#include <linux/usb/typec.h> | |
252 | +#include <linux/usb/typec_dp.h> | |
253 | +#include <linux/usb/typec_mux.h> | |
254 | +#include <linux/extcon-provider.h> | |
255 | + | |
256 | +struct typec_extcon { | |
257 | + struct device *dev; | |
258 | + | |
259 | + /* consumers */ | |
260 | + struct usb_role_switch *role_sw; | |
261 | + struct typec_switch *sw; | |
262 | + struct typec_mux *mux; | |
263 | + | |
264 | + /* providers */ | |
265 | + struct extcon_dev *extcon; | |
266 | + struct notifier_block extcon_nb; | |
267 | + | |
268 | + /* cached state from typec controller */ | |
269 | + enum usb_role role; | |
270 | + enum typec_orientation orientation; | |
271 | + struct typec_altmode alt; | |
272 | + unsigned long mode; | |
273 | + bool has_alt; | |
274 | + struct mutex lock; | |
275 | +}; | |
276 | + | |
277 | +static const unsigned int typec_extcon_cable[] = { | |
278 | + EXTCON_DISP_DP, | |
279 | + | |
280 | + EXTCON_USB, | |
281 | + EXTCON_USB_HOST, | |
282 | + | |
283 | + EXTCON_CHG_USB_SDP, | |
284 | + EXTCON_CHG_USB_CDP, | |
285 | + EXTCON_CHG_USB_DCP, | |
286 | + EXTCON_CHG_USB_ACA, | |
287 | + | |
288 | + EXTCON_NONE, | |
289 | +}; | |
290 | + | |
291 | +static void typec_extcon_set_cable(struct typec_extcon *tce, int id, bool on, | |
292 | + union extcon_property_value prop_ss, | |
293 | + union extcon_property_value prop_or) | |
8c9054af | 294 | +{ |
03acaa9b JP |
295 | + union extcon_property_value cur_ss, cur_or; |
296 | + bool prop_diff = false; | |
297 | + int ret; | |
298 | + | |
299 | + ret = extcon_get_property(tce->extcon, id, | |
300 | + EXTCON_PROP_USB_SS, &cur_ss); | |
301 | + if (ret || cur_ss.intval != prop_ss.intval) | |
302 | + prop_diff = true; | |
303 | + | |
304 | + ret = extcon_get_property(tce->extcon, id, | |
305 | + EXTCON_PROP_USB_TYPEC_POLARITY, &cur_or); | |
306 | + if (ret || cur_or.intval != prop_or.intval) | |
307 | + prop_diff = true; | |
308 | + | |
309 | + if (!on && extcon_get_state(tce->extcon, id)) { | |
310 | + extcon_set_state_sync(tce->extcon, id, false); | |
311 | + } else if (on && (!extcon_get_state(tce->extcon, id) || prop_diff)) { | |
312 | + extcon_set_state(tce->extcon, id, true); | |
313 | + extcon_set_property(tce->extcon, id, | |
314 | + EXTCON_PROP_USB_SS, prop_ss); | |
315 | + extcon_set_property(tce->extcon, id, | |
316 | + EXTCON_PROP_USB_TYPEC_POLARITY, prop_or); | |
317 | + extcon_sync(tce->extcon, id); | |
8c9054af JP |
318 | + } |
319 | +} | |
320 | + | |
03acaa9b | 321 | +static int typec_extcon_sync_extcon(struct typec_extcon *tce) |
8c9054af | 322 | +{ |
03acaa9b JP |
323 | + union extcon_property_value prop_ss, prop_or; |
324 | + bool has_dp = false; | |
8c9054af | 325 | + |
03acaa9b JP |
326 | + mutex_lock(&tce->lock); |
327 | + | |
328 | + /* connector is disconnected */ | |
329 | + if (tce->orientation == TYPEC_ORIENTATION_NONE) { | |
330 | + typec_extcon_set_cable(tce, EXTCON_USB, false, prop_ss, prop_or); | |
331 | + typec_extcon_set_cable(tce, EXTCON_USB_HOST, false, prop_ss, prop_or); | |
332 | + typec_extcon_set_cable(tce, EXTCON_DISP_DP, false, prop_ss, prop_or); | |
333 | + | |
334 | + extcon_set_state_sync(tce->extcon, EXTCON_CHG_USB_SDP, false); | |
335 | + extcon_set_state_sync(tce->extcon, EXTCON_CHG_USB_DCP, false); | |
336 | + extcon_set_state_sync(tce->extcon, EXTCON_CHG_USB_CDP, false); | |
337 | + extcon_set_state_sync(tce->extcon, EXTCON_CHG_USB_ACA, false); | |
338 | + | |
339 | + goto out_unlock; | |
8c9054af | 340 | + } |
03acaa9b JP |
341 | + |
342 | + prop_or.intval = tce->orientation == TYPEC_ORIENTATION_NORMAL ? 0 : 1; | |
343 | + prop_ss.intval = 0; | |
344 | + | |
345 | + if (tce->has_alt && tce->alt.svid == USB_TYPEC_DP_SID) { | |
346 | + switch (tce->mode) { | |
347 | + case TYPEC_STATE_SAFE: | |
348 | + break; | |
349 | + case TYPEC_DP_STATE_C: | |
350 | + case TYPEC_DP_STATE_E: | |
351 | + has_dp = true; | |
352 | + break; | |
353 | + case TYPEC_DP_STATE_D: | |
354 | + has_dp = true; | |
355 | + fallthrough; | |
356 | + case TYPEC_STATE_USB: | |
357 | + prop_ss.intval = 1; | |
358 | + break; | |
359 | + default: | |
360 | + dev_err(tce->dev, "unhandled mux mode=%lu\n", tce->mode); | |
361 | + break; | |
362 | + } | |
8c9054af | 363 | + } |
8c9054af | 364 | + |
03acaa9b JP |
365 | + typec_extcon_set_cable(tce, EXTCON_USB, |
366 | + tce->role == USB_ROLE_DEVICE, prop_ss, prop_or); | |
367 | + typec_extcon_set_cable(tce, EXTCON_USB_HOST, | |
368 | + tce->role == USB_ROLE_HOST, prop_ss, prop_or); | |
8c9054af | 369 | + |
03acaa9b JP |
370 | + typec_extcon_set_cable(tce, EXTCON_DISP_DP, has_dp, prop_ss, prop_or); |
371 | + | |
372 | +out_unlock: | |
373 | + mutex_unlock(&tce->lock); | |
374 | + return 0; | |
375 | +} | |
376 | + | |
377 | +static int typec_extcon_sw_set(struct typec_switch *sw, | |
378 | + enum typec_orientation orientation) | |
8c9054af | 379 | +{ |
03acaa9b JP |
380 | + struct typec_extcon *tce = typec_switch_get_drvdata(sw); |
381 | + | |
382 | + dev_dbg(tce->dev, "SW SET: orientation=%d\n", orientation); | |
383 | + | |
384 | + mutex_lock(&tce->lock); | |
385 | + tce->orientation = orientation; | |
386 | + mutex_unlock(&tce->lock); | |
387 | + | |
388 | + typec_extcon_sync_extcon(tce); | |
389 | + | |
390 | + return 0; | |
8c9054af JP |
391 | +} |
392 | + | |
03acaa9b JP |
393 | +static int typec_extcon_mux_set(struct typec_mux *mux, |
394 | + struct typec_mux_state *state) | |
8c9054af | 395 | +{ |
03acaa9b JP |
396 | + struct typec_extcon *tce = typec_mux_get_drvdata(mux); |
397 | + struct typec_altmode *alt = state->alt; | |
8c9054af | 398 | + |
03acaa9b JP |
399 | + dev_dbg(tce->dev, "MUX SET: state->mode=%lu\n", state->mode); |
400 | + if (alt) | |
401 | + dev_dbg(tce->dev, " ...alt: svid=%04hx mode=%d vdo=%08x active=%u\n", | |
402 | + alt->svid, alt->mode, alt->vdo, alt->active); | |
403 | + | |
404 | + mutex_lock(&tce->lock); | |
405 | + tce->mode = state->mode; | |
406 | + tce->has_alt = alt != NULL; | |
407 | + if (alt) | |
408 | + tce->alt = *alt; | |
409 | + mutex_unlock(&tce->lock); | |
410 | + | |
411 | + typec_extcon_sync_extcon(tce); | |
412 | + | |
413 | + return 0; | |
8c9054af JP |
414 | +} |
415 | + | |
03acaa9b JP |
416 | +static int typec_extcon_usb_set_role(struct usb_role_switch *sw, |
417 | + enum usb_role role) | |
418 | +{ | |
419 | + struct typec_extcon *tce = usb_role_switch_get_drvdata(sw); | |
420 | + | |
421 | + dev_dbg(tce->dev, "ROLE SET: role=%d\n", role); | |
422 | + | |
423 | + mutex_lock(&tce->lock); | |
424 | + tce->role = role; | |
425 | + mutex_unlock(&tce->lock); | |
426 | + | |
427 | + typec_extcon_sync_extcon(tce); | |
428 | + | |
429 | + return 0; | |
8c9054af JP |
430 | +} |
431 | + | |
03acaa9b JP |
432 | +static int typec_extcon_notifier(struct notifier_block *nb, |
433 | + unsigned long action, void *data) | |
434 | +{ | |
435 | + struct typec_extcon *tce = container_of(nb, struct typec_extcon, extcon_nb); | |
8c9054af | 436 | + |
03acaa9b JP |
437 | + bool sdp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_SDP); |
438 | + bool cdp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_CDP); | |
439 | + bool dcp = extcon_get_state(tce->extcon, EXTCON_CHG_USB_DCP); | |
440 | + bool usb = extcon_get_state(tce->extcon, EXTCON_USB); | |
441 | + bool usb_host = extcon_get_state(tce->extcon, EXTCON_USB_HOST); | |
442 | + bool dp = extcon_get_state(tce->extcon, EXTCON_DISP_DP); | |
8c9054af | 443 | + |
03acaa9b JP |
444 | + dev_info(tce->dev, "extcon changed sdp=%d cdp=%d dcp=%d usb=%d usb_host=%d dp=%d\n", |
445 | + sdp, cdp, dcp, usb, usb_host, dp); | |
8c9054af | 446 | + |
03acaa9b JP |
447 | + return NOTIFY_OK; |
448 | +} | |
8c9054af | 449 | + |
03acaa9b | 450 | +static int typec_extcon_probe(struct platform_device *pdev) |
8c9054af | 451 | +{ |
03acaa9b JP |
452 | + struct typec_switch_desc sw_desc = { }; |
453 | + struct typec_mux_desc mux_desc = { }; | |
454 | + struct usb_role_switch_desc role_desc = { }; | |
455 | + struct device *dev = &pdev->dev; | |
456 | + struct typec_extcon *tce; | |
457 | + int ret = 0; | |
8c9054af | 458 | + |
03acaa9b JP |
459 | + tce = devm_kzalloc(dev, sizeof(*tce), GFP_KERNEL); |
460 | + if (!tce) | |
461 | + return -ENOMEM; | |
8c9054af | 462 | + |
03acaa9b JP |
463 | + tce->dev = &pdev->dev; |
464 | + mutex_init(&tce->lock); | |
465 | + tce->mode = TYPEC_STATE_SAFE; | |
8c9054af | 466 | + |
03acaa9b JP |
467 | + sw_desc.drvdata = tce; |
468 | + sw_desc.fwnode = dev->fwnode; | |
469 | + sw_desc.set = typec_extcon_sw_set; | |
8c9054af | 470 | + |
03acaa9b JP |
471 | + tce->sw = typec_switch_register(dev, &sw_desc); |
472 | + if (IS_ERR(tce->sw)) | |
473 | + return dev_err_probe(dev, PTR_ERR(tce->sw), | |
474 | + "Error registering typec switch\n"); | |
8c9054af | 475 | + |
03acaa9b JP |
476 | + mux_desc.drvdata = tce; |
477 | + mux_desc.fwnode = dev->fwnode; | |
478 | + mux_desc.set = typec_extcon_mux_set; | |
479 | + | |
480 | + tce->mux = typec_mux_register(dev, &mux_desc); | |
481 | + if (IS_ERR(tce->mux)) { | |
482 | + ret = dev_err_probe(dev, PTR_ERR(tce->mux), | |
483 | + "Error registering typec mux\n"); | |
484 | + goto err_sw; | |
8c9054af | 485 | + } |
8c9054af | 486 | + |
03acaa9b JP |
487 | + role_desc.driver_data = tce; |
488 | + role_desc.fwnode = dev->fwnode; | |
489 | + role_desc.name = fwnode_get_name(dev->fwnode); | |
490 | + role_desc.set = typec_extcon_usb_set_role; | |
491 | + | |
492 | + tce->role_sw = usb_role_switch_register(dev, &role_desc); | |
493 | + if (IS_ERR(tce->role_sw)) { | |
494 | + ret = dev_err_probe(dev, PTR_ERR(tce->role_sw), | |
495 | + "Error registering USB role switch\n"); | |
496 | + goto err_mux; | |
8c9054af | 497 | + } |
03acaa9b JP |
498 | + |
499 | + tce->extcon = devm_extcon_dev_allocate(dev, typec_extcon_cable); | |
500 | + if (IS_ERR(tce->extcon)) { | |
501 | + ret = PTR_ERR(tce->extcon); | |
502 | + goto err_role; | |
8c9054af | 503 | + } |
03acaa9b JP |
504 | + |
505 | + ret = devm_extcon_dev_register(dev, tce->extcon); | |
506 | + if (ret) { | |
507 | + ret = dev_err_probe(dev, ret, "failed to register extcon device\n"); | |
508 | + goto err_role; | |
8c9054af | 509 | + } |
03acaa9b JP |
510 | + |
511 | + extcon_set_property_capability(tce->extcon, EXTCON_USB, | |
512 | + EXTCON_PROP_USB_SS); | |
513 | + extcon_set_property_capability(tce->extcon, EXTCON_USB, | |
514 | + EXTCON_PROP_USB_TYPEC_POLARITY); | |
515 | + extcon_set_property_capability(tce->extcon, EXTCON_USB_HOST, | |
516 | + EXTCON_PROP_USB_SS); | |
517 | + extcon_set_property_capability(tce->extcon, EXTCON_USB_HOST, | |
518 | + EXTCON_PROP_USB_TYPEC_POLARITY); | |
519 | + extcon_set_property_capability(tce->extcon, EXTCON_DISP_DP, | |
520 | + EXTCON_PROP_USB_SS); | |
521 | + extcon_set_property_capability(tce->extcon, EXTCON_DISP_DP, | |
522 | + EXTCON_PROP_USB_TYPEC_POLARITY); | |
523 | + | |
524 | + tce->extcon_nb.notifier_call = typec_extcon_notifier; | |
525 | + ret = devm_extcon_register_notifier_all(dev, tce->extcon, &tce->extcon_nb); | |
526 | + if (ret) { | |
527 | + dev_err_probe(dev, ret, "Failed to register extcon notifier\n"); | |
528 | + goto err_role; | |
8c9054af JP |
529 | + } |
530 | + | |
03acaa9b JP |
531 | + return 0; |
532 | + | |
533 | +err_role: | |
534 | + usb_role_switch_unregister(tce->role_sw); | |
535 | +err_mux: | |
536 | + typec_mux_unregister(tce->mux); | |
537 | +err_sw: | |
538 | + typec_switch_unregister(tce->sw); | |
539 | + return ret; | |
540 | +} | |
541 | + | |
542 | +static int typec_extcon_remove(struct platform_device *pdev) | |
543 | +{ | |
544 | + struct typec_extcon *tce = platform_get_drvdata(pdev); | |
545 | + | |
546 | + usb_role_switch_unregister(tce->role_sw); | |
547 | + typec_mux_unregister(tce->mux); | |
548 | + typec_switch_unregister(tce->sw); | |
549 | + | |
550 | + return 0; | |
551 | +} | |
552 | + | |
553 | +static struct of_device_id typec_extcon_of_match_table[] = { | |
554 | + { .compatible = "linux,typec-extcon-bridge" }, | |
555 | + { }, | |
556 | +}; | |
557 | +MODULE_DEVICE_TABLE(of, typec_extcon_of_match_table); | |
558 | + | |
559 | +static struct platform_driver typec_extcon_driver = { | |
560 | + .driver = { | |
561 | + .name = "typec-extcon", | |
562 | + .of_match_table = typec_extcon_of_match_table, | |
563 | + }, | |
564 | + .probe = typec_extcon_probe, | |
565 | + .remove = typec_extcon_remove, | |
566 | +}; | |
567 | + | |
568 | +module_platform_driver(typec_extcon_driver); | |
569 | + | |
570 | +MODULE_LICENSE("GPL"); | |
571 | +MODULE_AUTHOR("Ondrej Jirman <megous@megous.com>"); | |
572 | +MODULE_DESCRIPTION("typec -> extcon bridge driver"); | |
8c9054af | 573 | -- |
03acaa9b | 574 | 2.34.1 |
8c9054af | 575 | |
03acaa9b JP |
576 | From 4c839ce95766910235ff558b2959589c9068917c Mon Sep 17 00:00:00 2001 |
577 | From: Dan Johansen <strit@manjaro.org> | |
578 | Date: Sun, 2 Jan 2022 19:15:39 +0100 | |
579 | Subject: [PATCH] arm64: dts: rockchip: add typec extcon hack | |
8c9054af | 580 | |
03acaa9b | 581 | Signed-off-by: Dan Johansen <strit@manjaro.org> |
8c9054af | 582 | --- |
03acaa9b JP |
583 | .../boot/dts/rockchip/rk3399-pinebook-pro.dts | 31 +++++++++++++++++-- |
584 | 1 file changed, 29 insertions(+), 2 deletions(-) | |
8c9054af | 585 | |
03acaa9b JP |
586 | diff --git a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts |
587 | index c2f021a1a18f..fc33e111bbee 100644 | |
588 | --- a/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |
589 | +++ b/arch/arm64/boot/dts/rockchip/rk3399-pinebook-pro.dts | |
590 | @@ -384,6 +384,20 @@ mains_charger: dc-charger { | |
591 | pinctrl-names = "default"; | |
592 | pinctrl-0 = <&dc_det_pin>; | |
593 | }; | |
594 | + | |
595 | + typec_extcon_bridge: typec-extcon { | |
596 | + compatible = "linux,typec-extcon-bridge"; | |
597 | + usb-role-switch; | |
598 | + orientation-switch; | |
599 | + mode-switch; | |
600 | + svid = /bits/ 16 <0xff01>; | |
601 | + }; | |
602 | +}; | |
603 | + | |
604 | +&cdn_dp { | |
605 | + status = "okay"; | |
606 | + extcon = <&typec_extcon_bridge>; | |
607 | + phys = <&tcphy0_dp>; | |
608 | }; | |
609 | ||
610 | &cpu_b0 { | |
611 | @@ -705,6 +719,8 @@ fusb0: fusb30x@22 { | |
612 | pinctrl-names = "default"; | |
613 | pinctrl-0 = <&fusb0_int_pin>; | |
614 | vbus-supply = <&vbus_typec>; | |
615 | + extcon = <&typec_extcon_bridge>; | |
616 | + usb-role-switch = <&typec_extcon_bridge>; | |
617 | ||
618 | connector { | |
619 | compatible = "usb-c-connector"; | |
620 | @@ -713,10 +729,20 @@ connector { | |
621 | op-sink-microwatt = <1000000>; | |
622 | power-role = "dual"; | |
623 | sink-pdos = | |
624 | - <PDO_FIXED(5000, 2500, PDO_FIXED_USB_COMM)>; | |
625 | + <PDO_FIXED(5000, 2500, PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP)>; | |
626 | source-pdos = | |
627 | - <PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM)>; | |
628 | + <PDO_FIXED(5000, 1400, PDO_FIXED_USB_COMM | PDO_FIXED_DUAL_ROLE | PDO_FIXED_DATA_SWAP)>; | |
629 | try-power-role = "sink"; | |
630 | + | |
631 | + mode-switch = <&typec_extcon_bridge>; | |
632 | + orientation-switch = <&typec_extcon_bridge>; | |
633 | + | |
634 | + altmodes { | |
635 | + dp { | |
636 | + svid = <0xff01>; | |
637 | + vdo = <0x0c0046>; | |
638 | + }; | |
639 | + }; | |
640 | ||
641 | ports { | |
642 | #address-cells = <1>; | |
643 | @@ -984,6 +1010,7 @@ spiflash: flash@0 { | |
644 | }; | |
645 | ||
646 | &tcphy0 { | |
647 | + extcon = <&typec_extcon_bridge>; | |
648 | status = "okay"; | |
649 | }; | |
8c9054af | 650 | |
8c9054af | 651 | -- |
03acaa9b | 652 | 2.34.1 |
8c9054af | 653 | |
03acaa9b JP |
654 | From: Hugh Cole-Baker <sigmaris@gmail.com> |
655 | Subject: [PATCH v2 1/3] drm/rockchip: define gamma registers for RK3399 | |
656 | Date: Tue, 19 Oct 2021 22:58:41 +0100 | |
8c9054af | 657 | |
03acaa9b JP |
658 | The VOP on RK3399 has a different approach from previous versions for |
659 | setting a gamma lookup table, using an update_gamma_lut register. As | |
660 | this differs from RK3288, give RK3399 its own set of "common" register | |
661 | definitions. | |
8c9054af | 662 | |
03acaa9b | 663 | Signed-off-by: Hugh Cole-Baker <sigmaris@gmail.com> |
8c9054af | 664 | --- |
8c9054af | 665 | |
03acaa9b JP |
666 | Changes from v1: no changes in this patch |
667 | ||
668 | drivers/gpu/drm/rockchip/rockchip_drm_vop.h | 2 ++ | |
669 | drivers/gpu/drm/rockchip/rockchip_vop_reg.c | 24 +++++++++++++++++++-- | |
670 | drivers/gpu/drm/rockchip/rockchip_vop_reg.h | 1 + | |
671 | 3 files changed, 25 insertions(+), 2 deletions(-) | |
672 | ||
673 | diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h | |
674 | index 857d97cdc67c..14179e89bd21 100644 | |
675 | --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.h | |
676 | +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.h | |
677 | @@ -99,6 +99,8 @@ struct vop_common { | |
678 | struct vop_reg dither_down_en; | |
679 | struct vop_reg dither_up; | |
680 | struct vop_reg dsp_lut_en; | |
681 | + struct vop_reg update_gamma_lut; | |
682 | + struct vop_reg lut_buffer_index; | |
683 | struct vop_reg gate_en; | |
684 | struct vop_reg mmu_en; | |
685 | struct vop_reg out_mode; | |
686 | diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c | |
687 | index ca7cc82125cb..bfb7e130f09b 100644 | |
688 | --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.c | |
689 | +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.c | |
690 | @@ -865,6 +865,24 @@ static const struct vop_output rk3399_output = { | |
691 | .mipi_dual_channel_en = VOP_REG(RK3288_SYS_CTRL, 0x1, 3), | |
692 | }; | |
8c9054af | 693 | |
03acaa9b JP |
694 | +static const struct vop_common rk3399_common = { |
695 | + .standby = VOP_REG_SYNC(RK3399_SYS_CTRL, 0x1, 22), | |
696 | + .gate_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 23), | |
697 | + .mmu_en = VOP_REG(RK3399_SYS_CTRL, 0x1, 20), | |
698 | + .dither_down_sel = VOP_REG(RK3399_DSP_CTRL1, 0x1, 4), | |
699 | + .dither_down_mode = VOP_REG(RK3399_DSP_CTRL1, 0x1, 3), | |
700 | + .dither_down_en = VOP_REG(RK3399_DSP_CTRL1, 0x1, 2), | |
701 | + .pre_dither_down = VOP_REG(RK3399_DSP_CTRL1, 0x1, 1), | |
702 | + .dither_up = VOP_REG(RK3399_DSP_CTRL1, 0x1, 6), | |
703 | + .dsp_lut_en = VOP_REG(RK3399_DSP_CTRL1, 0x1, 0), | |
704 | + .update_gamma_lut = VOP_REG(RK3399_DSP_CTRL1, 0x1, 7), | |
705 | + .lut_buffer_index = VOP_REG(RK3399_DBG_POST_REG1, 0x1, 1), | |
706 | + .data_blank = VOP_REG(RK3399_DSP_CTRL0, 0x1, 19), | |
707 | + .dsp_blank = VOP_REG(RK3399_DSP_CTRL0, 0x3, 18), | |
708 | + .out_mode = VOP_REG(RK3399_DSP_CTRL0, 0xf, 0), | |
709 | + .cfg_done = VOP_REG_SYNC(RK3399_REG_CFG_DONE, 0x1, 0), | |
710 | +}; | |
711 | + | |
712 | static const struct vop_yuv2yuv_phy rk3399_yuv2yuv_win01_data = { | |
713 | .y2r_coefficients = { | |
714 | VOP_REG(RK3399_WIN0_YUV2YUV_Y2R + 0, 0xffff, 0), | |
715 | @@ -944,7 +962,7 @@ static const struct vop_data rk3399_vop_big = { | |
716 | .version = VOP_VERSION(3, 5), | |
717 | .feature = VOP_FEATURE_OUTPUT_RGB10, | |
718 | .intr = &rk3366_vop_intr, | |
719 | - .common = &rk3288_common, | |
720 | + .common = &rk3399_common, | |
721 | .modeset = &rk3288_modeset, | |
722 | .output = &rk3399_output, | |
723 | .afbc = &rk3399_vop_afbc, | |
724 | @@ -952,6 +970,7 @@ static const struct vop_data rk3399_vop_big = { | |
725 | .win = rk3399_vop_win_data, | |
726 | .win_size = ARRAY_SIZE(rk3399_vop_win_data), | |
727 | .win_yuv2yuv = rk3399_vop_big_win_yuv2yuv_data, | |
728 | + .lut_size = 1024, | |
729 | }; | |
730 | ||
731 | static const struct vop_win_data rk3399_vop_lit_win_data[] = { | |
732 | @@ -970,13 +989,14 @@ static const struct vop_win_yuv2yuv_data rk3399_vop_lit_win_yuv2yuv_data[] = { | |
733 | static const struct vop_data rk3399_vop_lit = { | |
734 | .version = VOP_VERSION(3, 6), | |
735 | .intr = &rk3366_vop_intr, | |
736 | - .common = &rk3288_common, | |
737 | + .common = &rk3399_common, | |
738 | .modeset = &rk3288_modeset, | |
739 | .output = &rk3399_output, | |
740 | .misc = &rk3368_misc, | |
741 | .win = rk3399_vop_lit_win_data, | |
742 | .win_size = ARRAY_SIZE(rk3399_vop_lit_win_data), | |
743 | .win_yuv2yuv = rk3399_vop_lit_win_yuv2yuv_data, | |
744 | + .lut_size = 256, | |
745 | }; | |
746 | ||
747 | static const struct vop_win_data rk3228_vop_win_data[] = { | |
748 | diff --git a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h | |
749 | index 0b3cd65ba5c1..406e981c75bd 100644 | |
750 | --- a/drivers/gpu/drm/rockchip/rockchip_vop_reg.h | |
751 | +++ b/drivers/gpu/drm/rockchip/rockchip_vop_reg.h | |
752 | @@ -628,6 +628,7 @@ | |
753 | #define RK3399_YUV2YUV_WIN 0x02c0 | |
754 | #define RK3399_YUV2YUV_POST 0x02c4 | |
755 | #define RK3399_AUTO_GATING_EN 0x02cc | |
756 | +#define RK3399_DBG_POST_REG1 0x036c | |
757 | #define RK3399_WIN0_CSC_COE 0x03a0 | |
758 | #define RK3399_WIN1_CSC_COE 0x03c0 | |
759 | #define RK3399_WIN2_CSC_COE 0x03e0 | |
760 | ||
761 | From: Hugh Cole-Baker <sigmaris@gmail.com> | |
762 | Subject: [PATCH v2 2/3] drm/rockchip: support gamma control on RK3399 | |
763 | Date: Tue, 19 Oct 2021 22:58:42 +0100 | |
764 | ||
765 | The RK3399 has a 1024-entry gamma LUT with 10 bits per component on its | |
766 | "big" VOP and a 256-entry, 8 bit per component LUT on the "little" VOP. | |
767 | Compared to the RK3288, it no longer requires disabling gamma while | |
768 | updating the LUT. On the RK3399, the LUT can be updated at any time as | |
769 | the hardware has two LUT buffers, one can be written while the other is | |
770 | in use. A swap of the buffers is triggered by writing 1 to the | |
771 | update_gamma_lut register. | |
772 | ||
773 | Signed-off-by: Hugh Cole-Baker <sigmaris@gmail.com> | |
774 | --- | |
775 | ||
776 | Changes from v1: Moved the vop_crtc_gamma_set call to the end of | |
777 | vop_crtc_atomic_enable after the clocks and CRTC are enabled. | |
778 | ||
779 | drivers/gpu/drm/rockchip/rockchip_drm_vop.c | 105 +++++++++++++------- | |
780 | 1 file changed, 71 insertions(+), 34 deletions(-) | |
781 | ||
782 | diff --git a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c | |
783 | index ba9e14da41b4..e2c97f1b26da 100644 | |
784 | --- a/drivers/gpu/drm/rockchip/rockchip_drm_vop.c | |
785 | +++ b/drivers/gpu/drm/rockchip/rockchip_drm_vop.c | |
786 | @@ -9,6 +9,7 @@ | |
8c9054af | 787 | #include <linux/delay.h> |
03acaa9b JP |
788 | #include <linux/iopoll.h> |
789 | #include <linux/kernel.h> | |
790 | +#include <linux/log2.h> | |
8c9054af | 791 | #include <linux/module.h> |
03acaa9b JP |
792 | #include <linux/of.h> |
793 | #include <linux/of_device.h> | |
794 | @@ -66,6 +67,9 @@ | |
795 | #define VOP_REG_SET(vop, group, name, v) \ | |
796 | vop_reg_set(vop, &vop->data->group->name, 0, ~0, v, #name) | |
8c9054af | 797 | |
03acaa9b JP |
798 | +#define VOP_HAS_REG(vop, group, name) \ |
799 | + (!!(vop->data->group->name.mask)) | |
8c9054af | 800 | + |
03acaa9b JP |
801 | #define VOP_INTR_SET_TYPE(vop, name, type, v) \ |
802 | do { \ | |
803 | int i, reg = 0, mask = 0; \ | |
804 | @@ -1204,17 +1208,22 @@ static bool vop_dsp_lut_is_enabled(struct vop *vop) | |
805 | return vop_read_reg(vop, 0, &vop->data->common->dsp_lut_en); | |
806 | } | |
807 | ||
808 | +static u32 vop_lut_buffer_index(struct vop *vop) | |
809 | +{ | |
810 | + return vop_read_reg(vop, 0, &vop->data->common->lut_buffer_index); | |
811 | +} | |
8c9054af | 812 | + |
03acaa9b JP |
813 | static void vop_crtc_write_gamma_lut(struct vop *vop, struct drm_crtc *crtc) |
814 | { | |
815 | struct drm_color_lut *lut = crtc->state->gamma_lut->data; | |
816 | - unsigned int i; | |
817 | + unsigned int i, bpc = ilog2(vop->data->lut_size); | |
818 | ||
819 | for (i = 0; i < crtc->gamma_size; i++) { | |
820 | u32 word; | |
821 | ||
822 | - word = (drm_color_lut_extract(lut[i].red, 10) << 20) | | |
823 | - (drm_color_lut_extract(lut[i].green, 10) << 10) | | |
824 | - drm_color_lut_extract(lut[i].blue, 10); | |
825 | + word = (drm_color_lut_extract(lut[i].red, bpc) << (2 * bpc)) | | |
826 | + (drm_color_lut_extract(lut[i].green, bpc) << bpc) | | |
827 | + drm_color_lut_extract(lut[i].blue, bpc); | |
828 | writel(word, vop->lut_regs + i * 4); | |
829 | } | |
830 | } | |
831 | @@ -1224,38 +1233,66 @@ static void vop_crtc_gamma_set(struct vop *vop, struct drm_crtc *crtc, | |
832 | { | |
833 | struct drm_crtc_state *state = crtc->state; | |
834 | unsigned int idle; | |
835 | + u32 lut_idx, old_idx; | |
836 | int ret; | |
837 | ||
838 | if (!vop->lut_regs) | |
839 | return; | |
840 | - /* | |
841 | - * To disable gamma (gamma_lut is null) or to write | |
842 | - * an update to the LUT, clear dsp_lut_en. | |
843 | - */ | |
844 | - spin_lock(&vop->reg_lock); | |
845 | - VOP_REG_SET(vop, common, dsp_lut_en, 0); | |
846 | - vop_cfg_done(vop); | |
847 | - spin_unlock(&vop->reg_lock); | |
848 | ||
849 | - /* | |
850 | - * In order to write the LUT to the internal memory, | |
851 | - * we need to first make sure the dsp_lut_en bit is cleared. | |
852 | - */ | |
853 | - ret = readx_poll_timeout(vop_dsp_lut_is_enabled, vop, | |
854 | - idle, !idle, 5, 30 * 1000); | |
855 | - if (ret) { | |
856 | - DRM_DEV_ERROR(vop->dev, "display LUT RAM enable timeout!\n"); | |
857 | - return; | |
858 | - } | |
859 | + if (!state->gamma_lut || !VOP_HAS_REG(vop, common, update_gamma_lut)) { | |
860 | + /* | |
861 | + * To disable gamma (gamma_lut is null) or to write | |
862 | + * an update to the LUT, clear dsp_lut_en. | |
863 | + */ | |
864 | + spin_lock(&vop->reg_lock); | |
865 | + VOP_REG_SET(vop, common, dsp_lut_en, 0); | |
866 | + vop_cfg_done(vop); | |
867 | + spin_unlock(&vop->reg_lock); | |
868 | ||
869 | - if (!state->gamma_lut) | |
870 | - return; | |
871 | + /* | |
872 | + * In order to write the LUT to the internal memory, | |
873 | + * we need to first make sure the dsp_lut_en bit is cleared. | |
874 | + */ | |
875 | + ret = readx_poll_timeout(vop_dsp_lut_is_enabled, vop, | |
876 | + idle, !idle, 5, 30 * 1000); | |
877 | + if (ret) { | |
878 | + DRM_DEV_ERROR(vop->dev, "display LUT RAM enable timeout!\n"); | |
879 | + return; | |
880 | + } | |
8c9054af | 881 | + |
03acaa9b JP |
882 | + if (!state->gamma_lut) |
883 | + return; | |
8c9054af | 884 | + } else { |
03acaa9b JP |
885 | + /* |
886 | + * On RK3399 the gamma LUT can updated without clearing dsp_lut_en, | |
887 | + * by setting update_gamma_lut then waiting for lut_buffer_index change | |
888 | + */ | |
889 | + old_idx = vop_lut_buffer_index(vop); | |
8c9054af | 890 | + } |
03acaa9b JP |
891 | |
892 | spin_lock(&vop->reg_lock); | |
893 | vop_crtc_write_gamma_lut(vop, crtc); | |
894 | VOP_REG_SET(vop, common, dsp_lut_en, 1); | |
895 | + VOP_REG_SET(vop, common, update_gamma_lut, 1); | |
896 | vop_cfg_done(vop); | |
897 | spin_unlock(&vop->reg_lock); | |
8c9054af | 898 | + |
03acaa9b JP |
899 | + if (VOP_HAS_REG(vop, common, update_gamma_lut)) { |
900 | + ret = readx_poll_timeout(vop_lut_buffer_index, vop, | |
901 | + lut_idx, lut_idx != old_idx, 5, 30 * 1000); | |
902 | + if (ret) { | |
903 | + DRM_DEV_ERROR(vop->dev, "gamma LUT update timeout!\n"); | |
904 | + return; | |
905 | + } | |
8c9054af | 906 | + |
03acaa9b JP |
907 | + /* |
908 | + * update_gamma_lut is auto cleared by HW, but write 0 to clear the bit | |
909 | + * in our backup of the regs. | |
910 | + */ | |
911 | + spin_lock(&vop->reg_lock); | |
912 | + VOP_REG_SET(vop, common, update_gamma_lut, 0); | |
913 | + spin_unlock(&vop->reg_lock); | |
914 | + } | |
915 | } | |
916 | ||
917 | static void vop_crtc_atomic_begin(struct drm_crtc *crtc, | |
918 | @@ -1305,14 +1342,6 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc, | |
919 | return; | |
8c9054af JP |
920 | } |
921 | ||
03acaa9b JP |
922 | - /* |
923 | - * If we have a GAMMA LUT in the state, then let's make sure | |
924 | - * it's updated. We might be coming out of suspend, | |
925 | - * which means the LUT internal memory needs to be re-written. | |
926 | - */ | |
927 | - if (crtc->state->gamma_lut) | |
928 | - vop_crtc_gamma_set(vop, crtc, old_state); | |
929 | - | |
930 | mutex_lock(&vop->vop_lock); | |
8c9054af | 931 | |
03acaa9b JP |
932 | WARN_ON(vop->event); |
933 | @@ -1403,6 +1432,14 @@ static void vop_crtc_atomic_enable(struct drm_crtc *crtc, | |
934 | ||
935 | VOP_REG_SET(vop, common, standby, 0); | |
936 | mutex_unlock(&vop->vop_lock); | |
937 | + | |
938 | + /* | |
939 | + * If we have a GAMMA LUT in the state, then let's make sure | |
940 | + * it's updated. We might be coming out of suspend, | |
941 | + * which means the LUT internal memory needs to be re-written. | |
942 | + */ | |
943 | + if (crtc->state->gamma_lut) | |
944 | + vop_crtc_gamma_set(vop, crtc, old_state); | |
945 | } | |
946 | ||
947 | static bool vop_fs_irq_is_pending(struct vop *vop) | |
948 | @@ -2125,8 +2162,8 @@ static int vop_bind(struct device *dev, struct device *master, void *data) | |
949 | ||
950 | res = platform_get_resource(pdev, IORESOURCE_MEM, 1); | |
951 | if (res) { | |
952 | - if (!vop_data->lut_size) { | |
953 | - DRM_DEV_ERROR(dev, "no gamma LUT size defined\n"); | |
954 | + if (vop_data->lut_size != 1024 && vop_data->lut_size != 256) { | |
955 | + DRM_DEV_ERROR(dev, "unsupported gamma LUT size %d\n", vop_data->lut_size); | |
956 | return -EINVAL; | |
957 | } | |
958 | vop->lut_regs = devm_ioremap_resource(dev, res); | |
8c9054af | 959 | |
03acaa9b JP |
960 | From: Hugh Cole-Baker <sigmaris@gmail.com> |
961 | Subject: [PATCH v2 3/3] arm64: dts: rockchip: enable gamma control on RK3399 | |
962 | Date: Tue, 19 Oct 2021 22:58:43 +0100 | |
8c9054af | 963 | |
03acaa9b JP |
964 | Define the memory region on RK3399 VOPs containing the gamma LUT at |
965 | base+0x2000. | |
8c9054af | 966 | |
03acaa9b | 967 | Signed-off-by: Hugh Cole-Baker <sigmaris@gmail.com> |
8c9054af | 968 | --- |
8c9054af | 969 | |
03acaa9b | 970 | Changes from v1: no changes in this patch |
8c9054af | 971 | |
03acaa9b JP |
972 | arch/arm64/boot/dts/rockchip/rk3399.dtsi | 4 ++-- |
973 | 1 file changed, 2 insertions(+), 2 deletions(-) | |
8c9054af | 974 | |
03acaa9b JP |
975 | diff --git a/arch/arm64/boot/dts/rockchip/rk3399.dtsi b/arch/arm64/boot/dts/rockchip/rk3399.dtsi |
976 | index 3871c7fd83b0..9cbf6ccdd256 100644 | |
977 | --- a/arch/arm64/boot/dts/rockchip/rk3399.dtsi | |
978 | +++ b/arch/arm64/boot/dts/rockchip/rk3399.dtsi | |
979 | @@ -1619,7 +1619,7 @@ i2s2: i2s@ff8a0000 { | |
980 | ||
981 | vopl: vop@ff8f0000 { | |
982 | compatible = "rockchip,rk3399-vop-lit"; | |
983 | - reg = <0x0 0xff8f0000 0x0 0x3efc>; | |
984 | + reg = <0x0 0xff8f0000 0x0 0x2000>, <0x0 0xff8f2000 0x0 0x400>; | |
985 | interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH 0>; | |
986 | assigned-clocks = <&cru ACLK_VOP1>, <&cru HCLK_VOP1>; | |
987 | assigned-clock-rates = <400000000>, <100000000>; | |
988 | @@ -1676,7 +1676,7 @@ vopl_mmu: iommu@ff8f3f00 { | |
989 | ||
990 | vopb: vop@ff900000 { | |
991 | compatible = "rockchip,rk3399-vop-big"; | |
992 | - reg = <0x0 0xff900000 0x0 0x3efc>; | |
993 | + reg = <0x0 0xff900000 0x0 0x2000>, <0x0 0xff902000 0x0 0x1000>; | |
994 | interrupts = <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH 0>; | |
995 | assigned-clocks = <&cru ACLK_VOP0>, <&cru HCLK_VOP0>; | |
996 | assigned-clock-rates = <400000000>, <100000000>; |