Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * Copyright (C) 2013 Red Hat
  4 * Author: Rob Clark <robdclark@gmail.com>
  5 */
  6
  7#include <linux/delay.h>
  8#include <linux/gpio/consumer.h>
  9#include <linux/pinctrl/consumer.h>
 10
 11#include "msm_kms.h"
 12#include "hdmi.h"
 13
 14struct hdmi_connector {
 15	struct drm_connector base;
 16	struct hdmi *hdmi;
 17	struct work_struct hpd_work;
 18};
 19#define to_hdmi_connector(x) container_of(x, struct hdmi_connector, base)
 20
 21static void msm_hdmi_phy_reset(struct hdmi *hdmi)
 22{
 23	unsigned int val;
 24
 25	val = hdmi_read(hdmi, REG_HDMI_PHY_CTRL);
 26
 27	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
 28		/* pull low */
 29		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
 30				val & ~HDMI_PHY_CTRL_SW_RESET);
 31	} else {
 32		/* pull high */
 33		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
 34				val | HDMI_PHY_CTRL_SW_RESET);
 35	}
 36
 37	if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
 38		/* pull low */
 39		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
 40				val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
 41	} else {
 42		/* pull high */
 43		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
 44				val | HDMI_PHY_CTRL_SW_RESET_PLL);
 45	}
 46
 47	msleep(100);
 48
 49	if (val & HDMI_PHY_CTRL_SW_RESET_LOW) {
 50		/* pull high */
 51		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
 52				val | HDMI_PHY_CTRL_SW_RESET);
 53	} else {
 54		/* pull low */
 55		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
 56				val & ~HDMI_PHY_CTRL_SW_RESET);
 57	}
 58
 59	if (val & HDMI_PHY_CTRL_SW_RESET_PLL_LOW) {
 60		/* pull high */
 61		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
 62				val | HDMI_PHY_CTRL_SW_RESET_PLL);
 63	} else {
 64		/* pull low */
 65		hdmi_write(hdmi, REG_HDMI_PHY_CTRL,
 66				val & ~HDMI_PHY_CTRL_SW_RESET_PLL);
 67	}
 68}
 69
 70static int gpio_config(struct hdmi *hdmi, bool on)
 71{
 72	const struct hdmi_platform_config *config = hdmi->config;
 73	int i;
 74
 75	if (on) {
 76		for (i = 0; i < HDMI_MAX_NUM_GPIO; i++) {
 77			struct hdmi_gpio_data gpio = config->gpios[i];
 78
 79			if (gpio.gpiod) {
 80				if (gpio.output) {
 81					gpiod_direction_output(gpio.gpiod,
 82							       gpio.value);
 83				} else {
 84					gpiod_direction_input(gpio.gpiod);
 85					gpiod_set_value_cansleep(gpio.gpiod,
 86								 gpio.value);
 87				}
 88			}
 89		}
 90
 91		DBG("gpio on");
 92	} else {
 93		for (i = 0; i < HDMI_MAX_NUM_GPIO; i++) {
 94			struct hdmi_gpio_data gpio = config->gpios[i];
 95
 96			if (!gpio.gpiod)
 97				continue;
 98
 99			if (gpio.output) {
100				int value = gpio.value ? 0 : 1;
101
102				gpiod_set_value_cansleep(gpio.gpiod, value);
103			}
104		};
105
106		DBG("gpio off");
107	}
108
109	return 0;
110}
111
112static void enable_hpd_clocks(struct hdmi *hdmi, bool enable)
113{
114	const struct hdmi_platform_config *config = hdmi->config;
115	struct device *dev = &hdmi->pdev->dev;
116	int i, ret;
117
118	if (enable) {
119		for (i = 0; i < config->hpd_clk_cnt; i++) {
120			if (config->hpd_freq && config->hpd_freq[i]) {
121				ret = clk_set_rate(hdmi->hpd_clks[i],
122						   config->hpd_freq[i]);
123				if (ret)
124					dev_warn(dev,
125						 "failed to set clk %s (%d)\n",
126						 config->hpd_clk_names[i], ret);
127			}
128
129			ret = clk_prepare_enable(hdmi->hpd_clks[i]);
130			if (ret) {
131				DRM_DEV_ERROR(dev,
132					"failed to enable hpd clk: %s (%d)\n",
133					config->hpd_clk_names[i], ret);
134			}
135		}
136	} else {
137		for (i = config->hpd_clk_cnt - 1; i >= 0; i--)
138			clk_disable_unprepare(hdmi->hpd_clks[i]);
139	}
140}
141
142int msm_hdmi_hpd_enable(struct drm_connector *connector)
143{
144	struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
145	struct hdmi *hdmi = hdmi_connector->hdmi;
146	const struct hdmi_platform_config *config = hdmi->config;
147	struct device *dev = &hdmi->pdev->dev;
148	uint32_t hpd_ctrl;
149	int i, ret;
150	unsigned long flags;
151
152	for (i = 0; i < config->hpd_reg_cnt; i++) {
153		ret = regulator_enable(hdmi->hpd_regs[i]);
154		if (ret) {
155			DRM_DEV_ERROR(dev, "failed to enable hpd regulator: %s (%d)\n",
156					config->hpd_reg_names[i], ret);
157			goto fail;
158		}
159	}
160
161	ret = pinctrl_pm_select_default_state(dev);
162	if (ret) {
163		DRM_DEV_ERROR(dev, "pinctrl state chg failed: %d\n", ret);
164		goto fail;
165	}
166
167	ret = gpio_config(hdmi, true);
168	if (ret) {
169		DRM_DEV_ERROR(dev, "failed to configure GPIOs: %d\n", ret);
170		goto fail;
171	}
172
173	pm_runtime_get_sync(dev);
174	enable_hpd_clocks(hdmi, true);
175
176	msm_hdmi_set_mode(hdmi, false);
177	msm_hdmi_phy_reset(hdmi);
178	msm_hdmi_set_mode(hdmi, true);
179
180	hdmi_write(hdmi, REG_HDMI_USEC_REFTIMER, 0x0001001b);
181
182	/* enable HPD events: */
183	hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
184			HDMI_HPD_INT_CTRL_INT_CONNECT |
185			HDMI_HPD_INT_CTRL_INT_EN);
186
187	/* set timeout to 4.1ms (max) for hardware debounce */
188	spin_lock_irqsave(&hdmi->reg_lock, flags);
189	hpd_ctrl = hdmi_read(hdmi, REG_HDMI_HPD_CTRL);
190	hpd_ctrl |= HDMI_HPD_CTRL_TIMEOUT(0x1fff);
191
192	/* Toggle HPD circuit to trigger HPD sense */
193	hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
194			~HDMI_HPD_CTRL_ENABLE & hpd_ctrl);
195	hdmi_write(hdmi, REG_HDMI_HPD_CTRL,
196			HDMI_HPD_CTRL_ENABLE | hpd_ctrl);
197	spin_unlock_irqrestore(&hdmi->reg_lock, flags);
198
199	return 0;
200
201fail:
202	return ret;
203}
204
205static void hdp_disable(struct hdmi_connector *hdmi_connector)
206{
207	struct hdmi *hdmi = hdmi_connector->hdmi;
208	const struct hdmi_platform_config *config = hdmi->config;
209	struct device *dev = &hdmi->pdev->dev;
210	int i, ret = 0;
211
212	/* Disable HPD interrupt */
213	hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, 0);
214
215	msm_hdmi_set_mode(hdmi, false);
216
217	enable_hpd_clocks(hdmi, false);
218	pm_runtime_put_autosuspend(dev);
219
220	ret = gpio_config(hdmi, false);
221	if (ret)
222		dev_warn(dev, "failed to unconfigure GPIOs: %d\n", ret);
223
224	ret = pinctrl_pm_select_sleep_state(dev);
225	if (ret)
226		dev_warn(dev, "pinctrl state chg failed: %d\n", ret);
227
228	for (i = 0; i < config->hpd_reg_cnt; i++) {
229		ret = regulator_disable(hdmi->hpd_regs[i]);
230		if (ret)
231			dev_warn(dev, "failed to disable hpd regulator: %s (%d)\n",
232					config->hpd_reg_names[i], ret);
233	}
234}
235
236static void
237msm_hdmi_hotplug_work(struct work_struct *work)
238{
239	struct hdmi_connector *hdmi_connector =
240		container_of(work, struct hdmi_connector, hpd_work);
241	struct drm_connector *connector = &hdmi_connector->base;
242	drm_helper_hpd_irq_event(connector->dev);
243}
244
245void msm_hdmi_connector_irq(struct drm_connector *connector)
246{
247	struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
248	struct hdmi *hdmi = hdmi_connector->hdmi;
249	uint32_t hpd_int_status, hpd_int_ctrl;
250
251	/* Process HPD: */
252	hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
253	hpd_int_ctrl   = hdmi_read(hdmi, REG_HDMI_HPD_INT_CTRL);
254
255	if ((hpd_int_ctrl & HDMI_HPD_INT_CTRL_INT_EN) &&
256			(hpd_int_status & HDMI_HPD_INT_STATUS_INT)) {
257		bool detected = !!(hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED);
258
259		/* ack & disable (temporarily) HPD events: */
260		hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL,
261			HDMI_HPD_INT_CTRL_INT_ACK);
262
263		DBG("status=%04x, ctrl=%04x", hpd_int_status, hpd_int_ctrl);
264
265		/* detect disconnect if we are connected or visa versa: */
266		hpd_int_ctrl = HDMI_HPD_INT_CTRL_INT_EN;
267		if (!detected)
268			hpd_int_ctrl |= HDMI_HPD_INT_CTRL_INT_CONNECT;
269		hdmi_write(hdmi, REG_HDMI_HPD_INT_CTRL, hpd_int_ctrl);
270
271		queue_work(hdmi->workq, &hdmi_connector->hpd_work);
272	}
273}
274
275static enum drm_connector_status detect_reg(struct hdmi *hdmi)
276{
277	uint32_t hpd_int_status;
278
279	pm_runtime_get_sync(&hdmi->pdev->dev);
280	enable_hpd_clocks(hdmi, true);
281
282	hpd_int_status = hdmi_read(hdmi, REG_HDMI_HPD_INT_STATUS);
283
284	enable_hpd_clocks(hdmi, false);
285	pm_runtime_put_autosuspend(&hdmi->pdev->dev);
286
287	return (hpd_int_status & HDMI_HPD_INT_STATUS_CABLE_DETECTED) ?
288			connector_status_connected : connector_status_disconnected;
289}
290
291#define HPD_GPIO_INDEX	2
292static enum drm_connector_status detect_gpio(struct hdmi *hdmi)
293{
294	const struct hdmi_platform_config *config = hdmi->config;
295	struct hdmi_gpio_data hpd_gpio = config->gpios[HPD_GPIO_INDEX];
296
297	return gpiod_get_value(hpd_gpio.gpiod) ?
298			connector_status_connected :
299			connector_status_disconnected;
300}
301
302static enum drm_connector_status hdmi_connector_detect(
303		struct drm_connector *connector, bool force)
304{
305	struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
306	struct hdmi *hdmi = hdmi_connector->hdmi;
307	const struct hdmi_platform_config *config = hdmi->config;
308	struct hdmi_gpio_data hpd_gpio = config->gpios[HPD_GPIO_INDEX];
309	enum drm_connector_status stat_gpio, stat_reg;
310	int retry = 20;
311
312	/*
313	 * some platforms may not have hpd gpio. Rely only on the status
314	 * provided by REG_HDMI_HPD_INT_STATUS in this case.
315	 */
316	if (!hpd_gpio.gpiod)
317		return detect_reg(hdmi);
318
319	do {
320		stat_gpio = detect_gpio(hdmi);
321		stat_reg  = detect_reg(hdmi);
322
323		if (stat_gpio == stat_reg)
324			break;
325
326		mdelay(10);
327	} while (--retry);
328
329	/* the status we get from reading gpio seems to be more reliable,
330	 * so trust that one the most if we didn't manage to get hdmi and
331	 * gpio status to agree:
332	 */
333	if (stat_gpio != stat_reg) {
334		DBG("HDMI_HPD_INT_STATUS tells us: %d", stat_reg);
335		DBG("hpd gpio tells us: %d", stat_gpio);
336	}
337
338	return stat_gpio;
339}
340
341static void hdmi_connector_destroy(struct drm_connector *connector)
342{
343	struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
344
345	hdp_disable(hdmi_connector);
346
347	drm_connector_cleanup(connector);
348
349	kfree(hdmi_connector);
350}
351
352static int msm_hdmi_connector_get_modes(struct drm_connector *connector)
353{
354	struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
355	struct hdmi *hdmi = hdmi_connector->hdmi;
356	struct edid *edid;
357	uint32_t hdmi_ctrl;
358	int ret = 0;
359
360	hdmi_ctrl = hdmi_read(hdmi, REG_HDMI_CTRL);
361	hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl | HDMI_CTRL_ENABLE);
362
363	edid = drm_get_edid(connector, hdmi->i2c);
364
365	hdmi_write(hdmi, REG_HDMI_CTRL, hdmi_ctrl);
366
367	hdmi->hdmi_mode = drm_detect_hdmi_monitor(edid);
368	drm_connector_update_edid_property(connector, edid);
369
370	if (edid) {
371		ret = drm_add_edid_modes(connector, edid);
372		kfree(edid);
373	}
374
375	return ret;
376}
377
378static int msm_hdmi_connector_mode_valid(struct drm_connector *connector,
379				 struct drm_display_mode *mode)
380{
381	struct hdmi_connector *hdmi_connector = to_hdmi_connector(connector);
382	struct hdmi *hdmi = hdmi_connector->hdmi;
383	const struct hdmi_platform_config *config = hdmi->config;
384	struct msm_drm_private *priv = connector->dev->dev_private;
385	struct msm_kms *kms = priv->kms;
386	long actual, requested;
387
388	requested = 1000 * mode->clock;
389	actual = kms->funcs->round_pixclk(kms,
390			requested, hdmi_connector->hdmi->encoder);
391
392	/* for mdp5/apq8074, we manage our own pixel clk (as opposed to
393	 * mdp4/dtv stuff where pixel clk is assigned to mdp/encoder
394	 * instead):
395	 */
396	if (config->pwr_clk_cnt > 0)
397		actual = clk_round_rate(hdmi->pwr_clks[0], actual);
398
399	DBG("requested=%ld, actual=%ld", requested, actual);
400
401	if (actual != requested)
402		return MODE_CLOCK_RANGE;
403
404	return 0;
405}
406
407static const struct drm_connector_funcs hdmi_connector_funcs = {
408	.detect = hdmi_connector_detect,
409	.fill_modes = drm_helper_probe_single_connector_modes,
410	.destroy = hdmi_connector_destroy,
411	.reset = drm_atomic_helper_connector_reset,
412	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
413	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
414};
415
416static const struct drm_connector_helper_funcs msm_hdmi_connector_helper_funcs = {
417	.get_modes = msm_hdmi_connector_get_modes,
418	.mode_valid = msm_hdmi_connector_mode_valid,
419};
420
421/* initialize connector */
422struct drm_connector *msm_hdmi_connector_init(struct hdmi *hdmi)
423{
424	struct drm_connector *connector = NULL;
425	struct hdmi_connector *hdmi_connector;
426
427	hdmi_connector = kzalloc(sizeof(*hdmi_connector), GFP_KERNEL);
428	if (!hdmi_connector)
429		return ERR_PTR(-ENOMEM);
430
431	hdmi_connector->hdmi = hdmi;
432	INIT_WORK(&hdmi_connector->hpd_work, msm_hdmi_hotplug_work);
433
434	connector = &hdmi_connector->base;
435
436	drm_connector_init(hdmi->dev, connector, &hdmi_connector_funcs,
437			DRM_MODE_CONNECTOR_HDMIA);
438	drm_connector_helper_add(connector, &msm_hdmi_connector_helper_funcs);
439
440	connector->polled = DRM_CONNECTOR_POLL_CONNECT |
441			DRM_CONNECTOR_POLL_DISCONNECT;
442
443	connector->interlace_allowed = 0;
444	connector->doublescan_allowed = 0;
445
446	drm_connector_attach_encoder(connector, hdmi->encoder);
447
448	return connector;
449}