Linux Audio

Check our new training course

Loading...
Note: File does not exist in v4.10.11.
  1// SPDX-License-Identifier: GPL-2.0-or-later
  2/*
  3 * Copyright (c) 2021-2022 Rockchip Electronics Co., Ltd.
  4 * Copyright (c) 2024 Collabora Ltd.
  5 *
  6 * Author: Algea Cao <algea.cao@rock-chips.com>
  7 * Author: Cristian Ciocaltea <cristian.ciocaltea@collabora.com>
  8 */
  9
 10#include <linux/clk.h>
 11#include <linux/gpio/consumer.h>
 12#include <linux/mfd/syscon.h>
 13#include <linux/module.h>
 14#include <linux/platform_device.h>
 15#include <linux/phy/phy.h>
 16#include <linux/regmap.h>
 17#include <linux/workqueue.h>
 18
 19#include <drm/bridge/dw_hdmi_qp.h>
 20#include <drm/display/drm_hdmi_helper.h>
 21#include <drm/drm_bridge_connector.h>
 22#include <drm/drm_of.h>
 23#include <drm/drm_probe_helper.h>
 24#include <drm/drm_simple_kms_helper.h>
 25
 26#include "rockchip_drm_drv.h"
 27
 28#define RK3588_GRF_SOC_CON2		0x0308
 29#define RK3588_HDMI0_HPD_INT_MSK	BIT(13)
 30#define RK3588_HDMI0_HPD_INT_CLR	BIT(12)
 31#define RK3588_GRF_SOC_CON7		0x031c
 32#define RK3588_SET_HPD_PATH_MASK	GENMASK(13, 12)
 33#define RK3588_GRF_SOC_STATUS1		0x0384
 34#define RK3588_HDMI0_LEVEL_INT		BIT(16)
 35#define RK3588_GRF_VO1_CON3		0x000c
 36#define RK3588_SCLIN_MASK		BIT(9)
 37#define RK3588_SDAIN_MASK		BIT(10)
 38#define RK3588_MODE_MASK		BIT(11)
 39#define RK3588_I2S_SEL_MASK		BIT(13)
 40#define RK3588_GRF_VO1_CON9		0x0024
 41#define RK3588_HDMI0_GRANT_SEL		BIT(10)
 42
 43#define HIWORD_UPDATE(val, mask)	((val) | (mask) << 16)
 44#define HOTPLUG_DEBOUNCE_MS		150
 45
 46struct rockchip_hdmi_qp {
 47	struct device *dev;
 48	struct regmap *regmap;
 49	struct regmap *vo_regmap;
 50	struct rockchip_encoder encoder;
 51	struct clk *ref_clk;
 52	struct dw_hdmi_qp *hdmi;
 53	struct phy *phy;
 54	struct gpio_desc *enable_gpio;
 55	struct delayed_work hpd_work;
 56};
 57
 58static struct rockchip_hdmi_qp *to_rockchip_hdmi_qp(struct drm_encoder *encoder)
 59{
 60	struct rockchip_encoder *rkencoder = to_rockchip_encoder(encoder);
 61
 62	return container_of(rkencoder, struct rockchip_hdmi_qp, encoder);
 63}
 64
 65static void dw_hdmi_qp_rockchip_encoder_enable(struct drm_encoder *encoder)
 66{
 67	struct rockchip_hdmi_qp *hdmi = to_rockchip_hdmi_qp(encoder);
 68	struct drm_crtc *crtc = encoder->crtc;
 69	unsigned long long rate;
 70
 71	/* Unconditionally switch to TMDS as FRL is not yet supported */
 72	gpiod_set_value(hdmi->enable_gpio, 1);
 73
 74	if (crtc && crtc->state) {
 75		rate = drm_hdmi_compute_mode_clock(&crtc->state->adjusted_mode,
 76						   8, HDMI_COLORSPACE_RGB);
 77		clk_set_rate(hdmi->ref_clk, rate);
 78		/*
 79		 * FIXME: Temporary workaround to pass pixel clock rate
 80		 * to the PHY driver until phy_configure_opts_hdmi
 81		 * becomes available in the PHY API. See also the related
 82		 * comment in rk_hdptx_phy_power_on() from
 83		 * drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c
 84		 */
 85		phy_set_bus_width(hdmi->phy, div_u64(rate, 100));
 86	}
 87}
 88
 89static int
 90dw_hdmi_qp_rockchip_encoder_atomic_check(struct drm_encoder *encoder,
 91					 struct drm_crtc_state *crtc_state,
 92					 struct drm_connector_state *conn_state)
 93{
 94	struct rockchip_crtc_state *s = to_rockchip_crtc_state(crtc_state);
 95
 96	s->output_mode = ROCKCHIP_OUT_MODE_AAAA;
 97	s->output_type = DRM_MODE_CONNECTOR_HDMIA;
 98
 99	return 0;
100}
101
102static const struct
103drm_encoder_helper_funcs dw_hdmi_qp_rockchip_encoder_helper_funcs = {
104	.enable		= dw_hdmi_qp_rockchip_encoder_enable,
105	.atomic_check	= dw_hdmi_qp_rockchip_encoder_atomic_check,
106};
107
108static int dw_hdmi_qp_rk3588_phy_init(struct dw_hdmi_qp *dw_hdmi, void *data)
109{
110	struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
111
112	return phy_power_on(hdmi->phy);
113}
114
115static void dw_hdmi_qp_rk3588_phy_disable(struct dw_hdmi_qp *dw_hdmi,
116					  void *data)
117{
118	struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
119
120	phy_power_off(hdmi->phy);
121}
122
123static enum drm_connector_status
124dw_hdmi_qp_rk3588_read_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
125{
126	struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
127	u32 val;
128
129	regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &val);
130
131	return val & RK3588_HDMI0_LEVEL_INT ?
132		connector_status_connected : connector_status_disconnected;
133}
134
135static void dw_hdmi_qp_rk3588_setup_hpd(struct dw_hdmi_qp *dw_hdmi, void *data)
136{
137	struct rockchip_hdmi_qp *hdmi = (struct rockchip_hdmi_qp *)data;
138
139	regmap_write(hdmi->regmap,
140		     RK3588_GRF_SOC_CON2,
141		     HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
142				   RK3588_HDMI0_HPD_INT_CLR |
143				   RK3588_HDMI0_HPD_INT_MSK));
144}
145
146static const struct dw_hdmi_qp_phy_ops rk3588_hdmi_phy_ops = {
147	.init		= dw_hdmi_qp_rk3588_phy_init,
148	.disable	= dw_hdmi_qp_rk3588_phy_disable,
149	.read_hpd	= dw_hdmi_qp_rk3588_read_hpd,
150	.setup_hpd	= dw_hdmi_qp_rk3588_setup_hpd,
151};
152
153static void dw_hdmi_qp_rk3588_hpd_work(struct work_struct *work)
154{
155	struct rockchip_hdmi_qp *hdmi = container_of(work,
156						     struct rockchip_hdmi_qp,
157						     hpd_work.work);
158	struct drm_device *drm = hdmi->encoder.encoder.dev;
159	bool changed;
160
161	if (drm) {
162		changed = drm_helper_hpd_irq_event(drm);
163		if (changed)
164			drm_dbg(hdmi, "connector status changed\n");
165	}
166}
167
168static irqreturn_t dw_hdmi_qp_rk3588_hardirq(int irq, void *dev_id)
169{
170	struct rockchip_hdmi_qp *hdmi = dev_id;
171	u32 intr_stat, val;
172
173	regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
174
175	if (intr_stat) {
176		val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK,
177				    RK3588_HDMI0_HPD_INT_MSK);
178		regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
179		return IRQ_WAKE_THREAD;
180	}
181
182	return IRQ_NONE;
183}
184
185static irqreturn_t dw_hdmi_qp_rk3588_irq(int irq, void *dev_id)
186{
187	struct rockchip_hdmi_qp *hdmi = dev_id;
188	u32 intr_stat, val;
189
190	regmap_read(hdmi->regmap, RK3588_GRF_SOC_STATUS1, &intr_stat);
191	if (!intr_stat)
192		return IRQ_NONE;
193
194	val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_CLR,
195			    RK3588_HDMI0_HPD_INT_CLR);
196	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
197
198	mod_delayed_work(system_wq, &hdmi->hpd_work,
199			 msecs_to_jiffies(HOTPLUG_DEBOUNCE_MS));
200
201	val |= HIWORD_UPDATE(0, RK3588_HDMI0_HPD_INT_MSK);
202	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
203
204	return IRQ_HANDLED;
205}
206
207static const struct of_device_id dw_hdmi_qp_rockchip_dt_ids[] = {
208	{ .compatible = "rockchip,rk3588-dw-hdmi-qp",
209	  .data = &rk3588_hdmi_phy_ops },
210	{},
211};
212MODULE_DEVICE_TABLE(of, dw_hdmi_qp_rockchip_dt_ids);
213
214static int dw_hdmi_qp_rockchip_bind(struct device *dev, struct device *master,
215				    void *data)
216{
217	static const char * const clk_names[] = {
218		"pclk", "earc", "aud", "hdp", "hclk_vo1",
219		"ref" /* keep "ref" last */
220	};
221	struct platform_device *pdev = to_platform_device(dev);
222	struct dw_hdmi_qp_plat_data plat_data;
223	struct drm_device *drm = data;
224	struct drm_connector *connector;
225	struct drm_encoder *encoder;
226	struct rockchip_hdmi_qp *hdmi;
227	struct clk *clk;
228	int ret, irq, i;
229	u32 val;
230
231	if (!pdev->dev.of_node)
232		return -ENODEV;
233
234	hdmi = devm_kzalloc(&pdev->dev, sizeof(*hdmi), GFP_KERNEL);
235	if (!hdmi)
236		return -ENOMEM;
237
238	plat_data.phy_ops = of_device_get_match_data(dev);
239	if (!plat_data.phy_ops)
240		return -ENODEV;
241
242	plat_data.phy_data = hdmi;
243	hdmi->dev = &pdev->dev;
244
245	encoder = &hdmi->encoder.encoder;
246	encoder->possible_crtcs = drm_of_find_possible_crtcs(drm, dev->of_node);
247
248	rockchip_drm_encoder_set_crtc_endpoint_id(&hdmi->encoder,
249						  dev->of_node, 0, 0);
250	/*
251	 * If we failed to find the CRTC(s) which this encoder is
252	 * supposed to be connected to, it's because the CRTC has
253	 * not been registered yet.  Defer probing, and hope that
254	 * the required CRTC is added later.
255	 */
256	if (encoder->possible_crtcs == 0)
257		return -EPROBE_DEFER;
258
259	hdmi->regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
260						       "rockchip,grf");
261	if (IS_ERR(hdmi->regmap)) {
262		drm_err(hdmi, "Unable to get rockchip,grf\n");
263		return PTR_ERR(hdmi->regmap);
264	}
265
266	hdmi->vo_regmap = syscon_regmap_lookup_by_phandle(dev->of_node,
267							  "rockchip,vo-grf");
268	if (IS_ERR(hdmi->vo_regmap)) {
269		drm_err(hdmi, "Unable to get rockchip,vo-grf\n");
270		return PTR_ERR(hdmi->vo_regmap);
271	}
272
273	for (i = 0; i < ARRAY_SIZE(clk_names); i++) {
274		clk = devm_clk_get_enabled(hdmi->dev, clk_names[i]);
275
276		if (IS_ERR(clk)) {
277			ret = PTR_ERR(clk);
278			if (ret != -EPROBE_DEFER)
279				drm_err(hdmi, "Failed to get %s clock: %d\n",
280					clk_names[i], ret);
281			return ret;
282		}
283	}
284	hdmi->ref_clk = clk;
285
286	hdmi->enable_gpio = devm_gpiod_get_optional(hdmi->dev, "enable",
287						    GPIOD_OUT_HIGH);
288	if (IS_ERR(hdmi->enable_gpio)) {
289		ret = PTR_ERR(hdmi->enable_gpio);
290		drm_err(hdmi, "Failed to request enable GPIO: %d\n", ret);
291		return ret;
292	}
293
294	hdmi->phy = devm_of_phy_get_by_index(dev, dev->of_node, 0);
295	if (IS_ERR(hdmi->phy)) {
296		ret = PTR_ERR(hdmi->phy);
297		if (ret != -EPROBE_DEFER)
298			drm_err(hdmi, "failed to get phy: %d\n", ret);
299		return ret;
300	}
301
302	val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
303	      HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
304	      HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
305	      HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
306	regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON3, val);
307
308	val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
309			    RK3588_SET_HPD_PATH_MASK);
310	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
311
312	val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
313			    RK3588_HDMI0_GRANT_SEL);
314	regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val);
315
316	val = HIWORD_UPDATE(RK3588_HDMI0_HPD_INT_MSK, RK3588_HDMI0_HPD_INT_MSK);
317	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON2, val);
318
319	INIT_DELAYED_WORK(&hdmi->hpd_work, dw_hdmi_qp_rk3588_hpd_work);
320
321	plat_data.main_irq = platform_get_irq_byname(pdev, "main");
322	if (plat_data.main_irq < 0)
323		return plat_data.main_irq;
324
325	irq = platform_get_irq_byname(pdev, "hpd");
326	if (irq < 0)
327		return irq;
328
329	ret = devm_request_threaded_irq(hdmi->dev, irq,
330					dw_hdmi_qp_rk3588_hardirq,
331					dw_hdmi_qp_rk3588_irq,
332					IRQF_SHARED, "dw-hdmi-qp-hpd",
333					hdmi);
334	if (ret)
335		return ret;
336
337	drm_encoder_helper_add(encoder, &dw_hdmi_qp_rockchip_encoder_helper_funcs);
338	drm_simple_encoder_init(drm, encoder, DRM_MODE_ENCODER_TMDS);
339
340	platform_set_drvdata(pdev, hdmi);
341
342	hdmi->hdmi = dw_hdmi_qp_bind(pdev, encoder, &plat_data);
343	if (IS_ERR(hdmi->hdmi)) {
344		ret = PTR_ERR(hdmi->hdmi);
345		drm_encoder_cleanup(encoder);
346		return ret;
347	}
348
349	connector = drm_bridge_connector_init(drm, encoder);
350	if (IS_ERR(connector)) {
351		ret = PTR_ERR(connector);
352		drm_err(hdmi, "failed to init bridge connector: %d\n", ret);
353		return ret;
354	}
355
356	return drm_connector_attach_encoder(connector, encoder);
357}
358
359static void dw_hdmi_qp_rockchip_unbind(struct device *dev,
360				       struct device *master,
361				       void *data)
362{
363	struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
364
365	cancel_delayed_work_sync(&hdmi->hpd_work);
366
367	drm_encoder_cleanup(&hdmi->encoder.encoder);
368}
369
370static const struct component_ops dw_hdmi_qp_rockchip_ops = {
371	.bind	= dw_hdmi_qp_rockchip_bind,
372	.unbind	= dw_hdmi_qp_rockchip_unbind,
373};
374
375static int dw_hdmi_qp_rockchip_probe(struct platform_device *pdev)
376{
377	return component_add(&pdev->dev, &dw_hdmi_qp_rockchip_ops);
378}
379
380static void dw_hdmi_qp_rockchip_remove(struct platform_device *pdev)
381{
382	component_del(&pdev->dev, &dw_hdmi_qp_rockchip_ops);
383}
384
385static int __maybe_unused dw_hdmi_qp_rockchip_resume(struct device *dev)
386{
387	struct rockchip_hdmi_qp *hdmi = dev_get_drvdata(dev);
388	u32 val;
389
390	val = HIWORD_UPDATE(RK3588_SCLIN_MASK, RK3588_SCLIN_MASK) |
391	      HIWORD_UPDATE(RK3588_SDAIN_MASK, RK3588_SDAIN_MASK) |
392	      HIWORD_UPDATE(RK3588_MODE_MASK, RK3588_MODE_MASK) |
393	      HIWORD_UPDATE(RK3588_I2S_SEL_MASK, RK3588_I2S_SEL_MASK);
394	regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON3, val);
395
396	val = HIWORD_UPDATE(RK3588_SET_HPD_PATH_MASK,
397			    RK3588_SET_HPD_PATH_MASK);
398	regmap_write(hdmi->regmap, RK3588_GRF_SOC_CON7, val);
399
400	val = HIWORD_UPDATE(RK3588_HDMI0_GRANT_SEL,
401			    RK3588_HDMI0_GRANT_SEL);
402	regmap_write(hdmi->vo_regmap, RK3588_GRF_VO1_CON9, val);
403
404	dw_hdmi_qp_resume(dev, hdmi->hdmi);
405
406	if (hdmi->encoder.encoder.dev)
407		drm_helper_hpd_irq_event(hdmi->encoder.encoder.dev);
408
409	return 0;
410}
411
412static const struct dev_pm_ops dw_hdmi_qp_rockchip_pm = {
413	SET_SYSTEM_SLEEP_PM_OPS(NULL, dw_hdmi_qp_rockchip_resume)
414};
415
416struct platform_driver dw_hdmi_qp_rockchip_pltfm_driver = {
417	.probe = dw_hdmi_qp_rockchip_probe,
418	.remove = dw_hdmi_qp_rockchip_remove,
419	.driver = {
420		.name = "dwhdmiqp-rockchip",
421		.pm = &dw_hdmi_qp_rockchip_pm,
422		.of_match_table = dw_hdmi_qp_rockchip_dt_ids,
423	},
424};