Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.2.
  1// SPDX-License-Identifier: GPL-2.0+
  2/*
  3 * Copyright (C) 2023 Loongson Technology Corporation Limited
  4 */
  5
  6#include <linux/delay.h>
  7
  8#include <drm/drm_atomic_helper.h>
  9#include <drm/drm_debugfs.h>
 10#include <drm/drm_edid.h>
 11#include <drm/drm_probe_helper.h>
 12
 13#include "lsdc_drv.h"
 14#include "lsdc_output.h"
 15
 16/*
 17 * The display controller in LS7A2000 has two display pipes
 18 * Display pipe 0 is attached with a built-in transparent VGA encoder and
 19 * a built-in HDMI encoder.
 20 * Display pipe 1 has only one built-in HDMI encoder connected.
 21 *       ______________________                          _____________
 22 *      |             +-----+  |                        |             |
 23 *      | CRTC0 -+--> | VGA |  ----> VGA Connector ---> | VGA Monitor |<---+
 24 *      |        |    +-----+  |                        |_____________|    |
 25 *      |        |             |                         ______________    |
 26 *      |        |    +------+ |                        |              |   |
 27 *      |        +--> | HDMI | ----> HDMI Connector --> | HDMI Monitor |<--+
 28 *      |             +------+ |                        |______________|   |
 29 *      |            +------+  |                                           |
 30 *      |            | i2c6 |  <-------------------------------------------+
 31 *      |            +------+  |
 32 *      |                      |
 33 *      |    DC in LS7A2000    |
 34 *      |                      |
 35 *      |            +------+  |
 36 *      |            | i2c7 |  <--------------------------------+
 37 *      |            +------+  |                                |
 38 *      |                      |                          ______|_______
 39 *      |            +------+  |                         |              |
 40 *      | CRTC1 ---> | HDMI |  ----> HDMI Connector ---> | HDMI Monitor |
 41 *      |            +------+  |                         |______________|
 42 *      |______________________|
 43 */
 44
 45static int ls7a2000_connector_get_modes(struct drm_connector *connector)
 46{
 47	int num;
 48
 49	if (connector->ddc) {
 50		const struct drm_edid *drm_edid;
 51
 52		drm_edid = drm_edid_read(connector);
 53		drm_edid_connector_update(connector, drm_edid);
 54		num = drm_edid_connector_add_modes(connector);
 55		drm_edid_free(drm_edid);
 56
 57		return num;
 58	}
 59
 60	num = drm_add_modes_noedid(connector, 1920, 1200);
 61
 62	drm_set_preferred_mode(connector, 1024, 768);
 63
 64	return num;
 65}
 66
 67static struct drm_encoder *
 68ls7a2000_connector_get_best_encoder(struct drm_connector *connector,
 69				    struct drm_atomic_state *state)
 70{
 71	struct lsdc_output *output = connector_to_lsdc_output(connector);
 72
 73	return &output->encoder;
 74}
 75
 76static const struct drm_connector_helper_funcs ls7a2000_connector_helpers = {
 77	.atomic_best_encoder = ls7a2000_connector_get_best_encoder,
 78	.get_modes = ls7a2000_connector_get_modes,
 79};
 80
 81/* debugfs */
 82
 83#define LSDC_HDMI_REG(i, reg) {                               \
 84	.name = __stringify_1(LSDC_HDMI##i##_##reg##_REG),    \
 85	.offset = LSDC_HDMI##i##_##reg##_REG,                 \
 86}
 87
 88static const struct lsdc_reg32 ls7a2000_hdmi0_encoder_regs[] = {
 89	LSDC_HDMI_REG(0, ZONE),
 90	LSDC_HDMI_REG(0, INTF_CTRL),
 91	LSDC_HDMI_REG(0, PHY_CTRL),
 92	LSDC_HDMI_REG(0, PHY_PLL),
 93	LSDC_HDMI_REG(0, AVI_INFO_CRTL),
 94	LSDC_HDMI_REG(0, PHY_CAL),
 95	LSDC_HDMI_REG(0, AUDIO_PLL_LO),
 96	LSDC_HDMI_REG(0, AUDIO_PLL_HI),
 97	{NULL, 0},  /* MUST be {NULL, 0} terminated */
 98};
 99
100static const struct lsdc_reg32 ls7a2000_hdmi1_encoder_regs[] = {
101	LSDC_HDMI_REG(1, ZONE),
102	LSDC_HDMI_REG(1, INTF_CTRL),
103	LSDC_HDMI_REG(1, PHY_CTRL),
104	LSDC_HDMI_REG(1, PHY_PLL),
105	LSDC_HDMI_REG(1, AVI_INFO_CRTL),
106	LSDC_HDMI_REG(1, PHY_CAL),
107	LSDC_HDMI_REG(1, AUDIO_PLL_LO),
108	LSDC_HDMI_REG(1, AUDIO_PLL_HI),
109	{NULL, 0},  /* MUST be {NULL, 0} terminated */
110};
111
112static int ls7a2000_hdmi_encoder_regs_show(struct seq_file *m, void *data)
113{
114	struct drm_info_node *node = (struct drm_info_node *)m->private;
115	struct drm_device *ddev = node->minor->dev;
116	struct lsdc_device *ldev = to_lsdc(ddev);
117	const struct lsdc_reg32 *preg;
118
119	preg = (const struct lsdc_reg32 *)node->info_ent->data;
120
121	while (preg->name) {
122		u32 offset = preg->offset;
123
124		seq_printf(m, "%s (0x%04x): 0x%08x\n",
125			   preg->name, offset, lsdc_rreg32(ldev, offset));
126		++preg;
127	}
128
129	return 0;
130}
131
132static const struct drm_info_list ls7a2000_hdmi0_debugfs_files[] = {
133	{ "regs", ls7a2000_hdmi_encoder_regs_show, 0, (void *)ls7a2000_hdmi0_encoder_regs },
134};
135
136static const struct drm_info_list ls7a2000_hdmi1_debugfs_files[] = {
137	{ "regs", ls7a2000_hdmi_encoder_regs_show, 0, (void *)ls7a2000_hdmi1_encoder_regs },
138};
139
140static void ls7a2000_hdmi0_late_register(struct drm_connector *connector,
141					 struct dentry *root)
142{
143	struct drm_device *ddev = connector->dev;
144	struct drm_minor *minor = ddev->primary;
145
146	drm_debugfs_create_files(ls7a2000_hdmi0_debugfs_files,
147				 ARRAY_SIZE(ls7a2000_hdmi0_debugfs_files),
148				 root, minor);
149}
150
151static void ls7a2000_hdmi1_late_register(struct drm_connector *connector,
152					 struct dentry *root)
153{
154	struct drm_device *ddev = connector->dev;
155	struct drm_minor *minor = ddev->primary;
156
157	drm_debugfs_create_files(ls7a2000_hdmi1_debugfs_files,
158				 ARRAY_SIZE(ls7a2000_hdmi1_debugfs_files),
159				 root, minor);
160}
161
162/* monitor present detection */
163
164static enum drm_connector_status
165ls7a2000_hdmi0_vga_connector_detect(struct drm_connector *connector, bool force)
166{
167	struct drm_device *ddev = connector->dev;
168	struct lsdc_device *ldev = to_lsdc(ddev);
169	u32 val;
170
171	val = lsdc_rreg32(ldev, LSDC_HDMI_HPD_STATUS_REG);
172
173	if (val & HDMI0_HPD_FLAG)
174		return connector_status_connected;
175
176	if (connector->ddc) {
177		if (drm_probe_ddc(connector->ddc))
178			return connector_status_connected;
179
180		return connector_status_disconnected;
181	}
182
183	return connector_status_unknown;
184}
185
186static enum drm_connector_status
187ls7a2000_hdmi1_connector_detect(struct drm_connector *connector, bool force)
188{
189	struct lsdc_device *ldev = to_lsdc(connector->dev);
190	u32 val;
191
192	val = lsdc_rreg32(ldev, LSDC_HDMI_HPD_STATUS_REG);
193
194	if (val & HDMI1_HPD_FLAG)
195		return connector_status_connected;
196
197	return connector_status_disconnected;
198}
199
200static const struct drm_connector_funcs ls7a2000_hdmi_connector_funcs[2] = {
201	{
202		.detect = ls7a2000_hdmi0_vga_connector_detect,
203		.fill_modes = drm_helper_probe_single_connector_modes,
204		.destroy = drm_connector_cleanup,
205		.reset = drm_atomic_helper_connector_reset,
206		.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
207		.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
208		.debugfs_init = ls7a2000_hdmi0_late_register,
209	},
210	{
211		.detect = ls7a2000_hdmi1_connector_detect,
212		.fill_modes = drm_helper_probe_single_connector_modes,
213		.destroy = drm_connector_cleanup,
214		.reset = drm_atomic_helper_connector_reset,
215		.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
216		.atomic_destroy_state = drm_atomic_helper_connector_destroy_state,
217		.debugfs_init = ls7a2000_hdmi1_late_register,
218	},
219};
220
221/* Even though some board has only one hdmi on display pipe 1,
222 * We still need hook lsdc_encoder_funcs up on display pipe 0,
223 * This is because we need its reset() callback get called, to
224 * set the LSDC_HDMIx_CTRL_REG using software gpio emulated i2c.
225 * Otherwise, the firmware may set LSDC_HDMIx_CTRL_REG blindly.
226 */
227static void ls7a2000_hdmi0_encoder_reset(struct drm_encoder *encoder)
228{
229	struct drm_device *ddev = encoder->dev;
230	struct lsdc_device *ldev = to_lsdc(ddev);
231	u32 val;
232
233	val = PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN;
234	lsdc_wreg32(ldev, LSDC_CRTC0_DVO_CONF_REG, val);
235
236	/* using software gpio emulated i2c */
237	val = lsdc_rreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG);
238	val &= ~HW_I2C_EN;
239	lsdc_wreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG, val);
240
241	/* help the hdmi phy to get out of reset state */
242	lsdc_wreg32(ldev, LSDC_HDMI0_PHY_CTRL_REG, HDMI_PHY_RESET_N);
243
244	mdelay(20);
245
246	drm_dbg(ddev, "HDMI-0 Reset\n");
247}
248
249static void ls7a2000_hdmi1_encoder_reset(struct drm_encoder *encoder)
250{
251	struct drm_device *ddev = encoder->dev;
252	struct lsdc_device *ldev = to_lsdc(ddev);
253	u32 val;
254
255	val = PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN;
256	lsdc_wreg32(ldev, LSDC_CRTC1_DVO_CONF_REG, val);
257
258	/* using software gpio emulated i2c */
259	val = lsdc_rreg32(ldev, LSDC_HDMI1_INTF_CTRL_REG);
260	val &= ~HW_I2C_EN;
261	lsdc_wreg32(ldev, LSDC_HDMI1_INTF_CTRL_REG, val);
262
263	/*  help the hdmi phy to get out of reset state */
264	lsdc_wreg32(ldev, LSDC_HDMI1_PHY_CTRL_REG, HDMI_PHY_RESET_N);
265
266	mdelay(20);
267
268	drm_dbg(ddev, "HDMI-1 Reset\n");
269}
270
271static const struct drm_encoder_funcs ls7a2000_encoder_funcs[2] = {
272	{
273		.reset = ls7a2000_hdmi0_encoder_reset,
274		.destroy = drm_encoder_cleanup,
275	},
276	{
277		.reset = ls7a2000_hdmi1_encoder_reset,
278		.destroy = drm_encoder_cleanup,
279	},
280};
281
282static int ls7a2000_hdmi_set_avi_infoframe(struct drm_encoder *encoder,
283					   struct drm_display_mode *mode)
284{
285	struct lsdc_output *output = encoder_to_lsdc_output(encoder);
286	struct lsdc_display_pipe *dispipe = output_to_display_pipe(output);
287	unsigned int index = dispipe->index;
288	struct drm_device *ddev = encoder->dev;
289	struct lsdc_device *ldev = to_lsdc(ddev);
290	struct hdmi_avi_infoframe infoframe;
291	u8 buffer[HDMI_INFOFRAME_SIZE(AVI)];
292	unsigned char *ptr = &buffer[HDMI_INFOFRAME_HEADER_SIZE];
293	unsigned int content0, content1, content2, content3;
294	int err;
295
296	err = drm_hdmi_avi_infoframe_from_display_mode(&infoframe,
297						       &output->connector,
298						       mode);
299	if (err < 0) {
300		drm_err(ddev, "failed to setup AVI infoframe: %d\n", err);
301		return err;
302	}
303
304	/* Fixed infoframe configuration not linked to the mode */
305	infoframe.colorspace = HDMI_COLORSPACE_RGB;
306	infoframe.quantization_range = HDMI_QUANTIZATION_RANGE_DEFAULT;
307	infoframe.colorimetry = HDMI_COLORIMETRY_NONE;
308
309	err = hdmi_avi_infoframe_pack(&infoframe, buffer, sizeof(buffer));
310	if (err < 0) {
311		drm_err(ddev, "failed to pack AVI infoframe: %d\n", err);
312			return err;
313	}
314
315	content0 = *(unsigned int *)ptr;
316	content1 = *(ptr + 4);
317	content2 = *(unsigned int *)(ptr + 5);
318	content3 = *(unsigned int *)(ptr + 9);
319
320	lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_CONTENT0, index, content0);
321	lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_CONTENT1, index, content1);
322	lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_CONTENT2, index, content2);
323	lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_CONTENT3, index, content3);
324
325	lsdc_pipe_wreg32(ldev, LSDC_HDMI0_AVI_INFO_CRTL_REG, index,
326			 AVI_PKT_ENABLE | AVI_PKT_UPDATE);
327
328	drm_dbg(ddev, "Update HDMI-%u avi infoframe\n", index);
329
330	return 0;
331}
332
333static void ls7a2000_hdmi_atomic_disable(struct drm_encoder *encoder,
334					 struct drm_atomic_state *state)
335{
336	struct lsdc_output *output = encoder_to_lsdc_output(encoder);
337	struct lsdc_display_pipe *dispipe = output_to_display_pipe(output);
338	unsigned int index = dispipe->index;
339	struct drm_device *ddev = encoder->dev;
340	struct lsdc_device *ldev = to_lsdc(ddev);
341	u32 val;
342
343	/* Disable the hdmi phy */
344	val = lsdc_pipe_rreg32(ldev, LSDC_HDMI0_PHY_CTRL_REG, index);
345	val &= ~HDMI_PHY_EN;
346	lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_CTRL_REG, index, val);
347
348	/* Disable the hdmi interface */
349	val = lsdc_pipe_rreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG, index);
350	val &= ~HDMI_INTERFACE_EN;
351	lsdc_pipe_wreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG, index, val);
352
353	drm_dbg(ddev, "HDMI-%u disabled\n", index);
354}
355
356static void ls7a2000_hdmi_atomic_enable(struct drm_encoder *encoder,
357					struct drm_atomic_state *state)
358{
359	struct drm_device *ddev = encoder->dev;
360	struct lsdc_device *ldev = to_lsdc(ddev);
361	struct lsdc_output *output = encoder_to_lsdc_output(encoder);
362	struct lsdc_display_pipe *dispipe = output_to_display_pipe(output);
363	unsigned int index = dispipe->index;
364	u32 val;
365
366	/* datasheet say it should larger than 48 */
367	val = 64 << HDMI_H_ZONE_IDLE_SHIFT | 64 << HDMI_V_ZONE_IDLE_SHIFT;
368
369	lsdc_pipe_wreg32(ldev, LSDC_HDMI0_ZONE_REG, index, val);
370
371	val = HDMI_PHY_TERM_STATUS |
372	      HDMI_PHY_TERM_DET_EN |
373	      HDMI_PHY_TERM_H_EN |
374	      HDMI_PHY_TERM_L_EN |
375	      HDMI_PHY_RESET_N |
376	      HDMI_PHY_EN;
377
378	lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_CTRL_REG, index, val);
379
380	udelay(2);
381
382	val = HDMI_CTL_PERIOD_MODE |
383	      HDMI_AUDIO_EN |
384	      HDMI_PACKET_EN |
385	      HDMI_INTERFACE_EN |
386	      (8 << HDMI_VIDEO_PREAMBLE_SHIFT);
387
388	lsdc_pipe_wreg32(ldev, LSDC_HDMI0_INTF_CTRL_REG, index, val);
389
390	drm_dbg(ddev, "HDMI-%u enabled\n", index);
391}
392
393/*
394 *  Fout = M * Fin
395 *
396 *  M = (4 * LF) / (IDF * ODF)
397 *
398 *  IDF: Input Division Factor
399 *  ODF: Output Division Factor
400 *   LF: Loop Factor
401 *    M: Required Mult
402 *
403 *  +--------------------------------------------------------+
404 *  |     Fin (kHZ)     | M  | IDF | LF | ODF |   Fout(Mhz)  |
405 *  |-------------------+----+-----+----+-----+--------------|
406 *  |  170000 ~ 340000  | 10 | 16  | 40 |  1  | 1700 ~ 3400  |
407 *  |   85000 ~ 170000  | 10 |  8  | 40 |  2  |  850 ~ 1700  |
408 *  |   42500 ~  85000  | 10 |  4  | 40 |  4  |  425 ~ 850   |
409 *  |   21250 ~  42500  | 10 |  2  | 40 |  8  | 212.5 ~ 425  |
410 *  |   20000 ~  21250  | 10 |  1  | 40 | 16  |  200 ~ 212.5 |
411 *  +--------------------------------------------------------+
412 */
413static void ls7a2000_hdmi_phy_pll_config(struct lsdc_device *ldev,
414					 int fin,
415					 unsigned int index)
416{
417	struct drm_device *ddev = &ldev->base;
418	int count = 0;
419	u32 val;
420
421	/* Firstly, disable phy pll */
422	lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, index, 0x0);
423
424	/*
425	 * Most of time, loongson HDMI require M = 10
426	 * for example, 10 = (4 * 40) / (8 * 2)
427	 * here, write "1" to the ODF will get "2"
428	 */
429
430	if (fin >= 170000)
431		val = (16 << HDMI_PLL_IDF_SHIFT) |
432		      (40 << HDMI_PLL_LF_SHIFT) |
433		      (0 << HDMI_PLL_ODF_SHIFT);
434	else if (fin >= 85000)
435		val = (8 << HDMI_PLL_IDF_SHIFT) |
436		      (40 << HDMI_PLL_LF_SHIFT) |
437		      (1 << HDMI_PLL_ODF_SHIFT);
438	else if (fin >= 42500)
439		val = (4 << HDMI_PLL_IDF_SHIFT) |
440		      (40 << HDMI_PLL_LF_SHIFT) |
441		      (2 << HDMI_PLL_ODF_SHIFT);
442	else if  (fin >= 21250)
443		val = (2 << HDMI_PLL_IDF_SHIFT) |
444		      (40 << HDMI_PLL_LF_SHIFT) |
445		      (3 << HDMI_PLL_ODF_SHIFT);
446	else
447		val = (1 << HDMI_PLL_IDF_SHIFT) |
448		      (40 << HDMI_PLL_LF_SHIFT) |
449		      (4 << HDMI_PLL_ODF_SHIFT);
450
451	lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, index, val);
452
453	val |= HDMI_PLL_ENABLE;
454
455	lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, index, val);
456
457	udelay(2);
458
459	drm_dbg(ddev, "Fin of HDMI-%u: %d kHz\n", index, fin);
460
461	/* Wait hdmi phy pll lock */
462	do {
463		val = lsdc_pipe_rreg32(ldev, LSDC_HDMI0_PHY_PLL_REG, index);
464
465		if (val & HDMI_PLL_LOCKED) {
466			drm_dbg(ddev, "Setting HDMI-%u PLL take %d cycles\n",
467				index, count);
468			break;
469		}
470		++count;
471	} while (count < 1000);
472
473	lsdc_pipe_wreg32(ldev, LSDC_HDMI0_PHY_CAL_REG, index, 0x0f000ff0);
474
475	if (count >= 1000)
476		drm_err(ddev, "Setting HDMI-%u PLL failed\n", index);
477}
478
479static void ls7a2000_hdmi_atomic_mode_set(struct drm_encoder *encoder,
480					  struct drm_crtc_state *crtc_state,
481					  struct drm_connector_state *conn_state)
482{
483	struct lsdc_output *output = encoder_to_lsdc_output(encoder);
484	struct lsdc_display_pipe *dispipe = output_to_display_pipe(output);
485	unsigned int index = dispipe->index;
486	struct drm_device *ddev = encoder->dev;
487	struct lsdc_device *ldev = to_lsdc(ddev);
488	struct drm_display_mode *mode = &crtc_state->mode;
489
490	ls7a2000_hdmi_phy_pll_config(ldev, mode->clock, index);
491
492	ls7a2000_hdmi_set_avi_infoframe(encoder, mode);
493
494	drm_dbg(ddev, "%s modeset finished\n", encoder->name);
495}
496
497static const struct drm_encoder_helper_funcs ls7a2000_encoder_helper_funcs = {
498	.atomic_disable = ls7a2000_hdmi_atomic_disable,
499	.atomic_enable = ls7a2000_hdmi_atomic_enable,
500	.atomic_mode_set = ls7a2000_hdmi_atomic_mode_set,
501};
502
503/*
504 * For LS7A2000:
505 *
506 * 1) Most of board export one vga + hdmi output interface.
507 * 2) Yet, Some boards export double hdmi output interface.
508 * 3) Still have boards export three output(2 hdmi + 1 vga).
509 *
510 * So let's hook hdmi helper funcs to all display pipe, don't miss.
511 * writing hdmi register do no harms.
512 */
513int ls7a2000_output_init(struct drm_device *ddev,
514			 struct lsdc_display_pipe *dispipe,
515			 struct i2c_adapter *ddc,
516			 unsigned int pipe)
517{
518	struct lsdc_output *output = &dispipe->output;
519	struct drm_encoder *encoder = &output->encoder;
520	struct drm_connector *connector = &output->connector;
521	int ret;
522
523	ret = drm_encoder_init(ddev, encoder, &ls7a2000_encoder_funcs[pipe],
524			       DRM_MODE_ENCODER_TMDS, "encoder-%u", pipe);
525	if (ret)
526		return ret;
527
528	encoder->possible_crtcs = BIT(pipe);
529
530	drm_encoder_helper_add(encoder, &ls7a2000_encoder_helper_funcs);
531
532	ret = drm_connector_init_with_ddc(ddev, connector,
533					  &ls7a2000_hdmi_connector_funcs[pipe],
534					  DRM_MODE_CONNECTOR_HDMIA, ddc);
535	if (ret)
536		return ret;
537
538	drm_info(ddev, "display pipe-%u has HDMI %s\n", pipe, pipe ? "" : "and/or VGA");
539
540	drm_connector_helper_add(connector, &ls7a2000_connector_helpers);
541
542	drm_connector_attach_encoder(connector, encoder);
543
544	connector->polled = DRM_CONNECTOR_POLL_CONNECT |
545			    DRM_CONNECTOR_POLL_DISCONNECT;
546
547	connector->interlace_allowed = 0;
548	connector->doublescan_allowed = 0;
549
550	return 0;
551}