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 <drm/drm_atomic_helper.h>
  7#include <drm/drm_edid.h>
  8#include <drm/drm_probe_helper.h>
  9
 10#include "lsdc_drv.h"
 11#include "lsdc_output.h"
 12
 13/*
 14 * The display controller in the LS7A1000 exports two DVO interfaces, thus
 15 * external encoder is required, except connected to the DPI panel directly.
 16 *
 17 *       ___________________                                     _________
 18 *      |            -------|                                   |         |
 19 *      |  CRTC0 --> | DVO0 ----> Encoder0 ---> Connector0 ---> | Display |
 20 *      |  _   _     -------|        ^             ^            |_________|
 21 *      | | | | |  +------+ |        |             |
 22 *      | |_| |_|  | i2c6 | <--------+-------------+
 23 *      |          +------+ |
 24 *      |                   |
 25 *      |  DC in LS7A1000   |
 26 *      |                   |
 27 *      |  _   _   +------+ |
 28 *      | | | | |  | i2c7 | <--------+-------------+
 29 *      | |_| |_|  +------+ |        |             |             _________
 30 *      |            -------|        |             |            |         |
 31 *      |  CRTC1 --> | DVO1 ----> Encoder1 ---> Connector1 ---> |  Panel  |
 32 *      |            -------|                                   |_________|
 33 *      |___________________|
 34 *
 35 * Currently, we assume the external encoders connected to the DVO are
 36 * transparent. Loongson's DVO interface can directly drive RGB888 panels.
 37 *
 38 *  TODO: Add support for non-transparent encoders
 39 */
 40
 41static int ls7a1000_dpi_connector_get_modes(struct drm_connector *conn)
 42{
 43	int num;
 44
 45	if (conn->ddc) {
 46		const struct drm_edid *drm_edid;
 47
 48		drm_edid = drm_edid_read(conn);
 49		drm_edid_connector_update(conn, drm_edid);
 50		num = drm_edid_connector_add_modes(conn);
 51		drm_edid_free(drm_edid);
 52
 53		return num;
 54	}
 55
 56	num = drm_add_modes_noedid(conn, 1920, 1200);
 57
 58	drm_set_preferred_mode(conn, 1024, 768);
 59
 60	return num;
 61}
 62
 63static struct drm_encoder *
 64ls7a1000_dpi_connector_get_best_encoder(struct drm_connector *connector,
 65					struct drm_atomic_state *state)
 66{
 67	struct lsdc_output *output = connector_to_lsdc_output(connector);
 68
 69	return &output->encoder;
 70}
 71
 72static const struct drm_connector_helper_funcs
 73ls7a1000_dpi_connector_helpers = {
 74	.atomic_best_encoder = ls7a1000_dpi_connector_get_best_encoder,
 75	.get_modes = ls7a1000_dpi_connector_get_modes,
 76};
 77
 78static enum drm_connector_status
 79ls7a1000_dpi_connector_detect(struct drm_connector *connector, bool force)
 80{
 81	struct i2c_adapter *ddc = connector->ddc;
 82
 83	if (ddc) {
 84		if (drm_probe_ddc(ddc))
 85			return connector_status_connected;
 86
 87		return connector_status_disconnected;
 88	}
 89
 90	return connector_status_unknown;
 91}
 92
 93static const struct drm_connector_funcs ls7a1000_dpi_connector_funcs = {
 94	.detect = ls7a1000_dpi_connector_detect,
 95	.fill_modes = drm_helper_probe_single_connector_modes,
 96	.destroy = drm_connector_cleanup,
 97	.reset = drm_atomic_helper_connector_reset,
 98	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
 99	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state
100};
101
102static void ls7a1000_pipe0_encoder_reset(struct drm_encoder *encoder)
103{
104	struct drm_device *ddev = encoder->dev;
105	struct lsdc_device *ldev = to_lsdc(ddev);
106
107	/*
108	 * We need this for S3 support, screen will not lightup if don't set
109	 * this register correctly.
110	 */
111	lsdc_wreg32(ldev, LSDC_CRTC0_DVO_CONF_REG,
112		    PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN);
113}
114
115static void ls7a1000_pipe1_encoder_reset(struct drm_encoder *encoder)
116{
117	struct drm_device *ddev = encoder->dev;
118	struct lsdc_device *ldev = to_lsdc(ddev);
119
120	/*
121	 * We need this for S3 support, screen will not lightup if don't set
122	 * this register correctly.
123	 */
124
125	/* DVO */
126	lsdc_wreg32(ldev, LSDC_CRTC1_DVO_CONF_REG,
127		    BIT(31) | PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN);
128}
129
130static const struct drm_encoder_funcs ls7a1000_encoder_funcs[2] = {
131	{
132		.reset = ls7a1000_pipe0_encoder_reset,
133		.destroy = drm_encoder_cleanup,
134	},
135	{
136		.reset = ls7a1000_pipe1_encoder_reset,
137		.destroy = drm_encoder_cleanup,
138	},
139};
140
141int ls7a1000_output_init(struct drm_device *ddev,
142			 struct lsdc_display_pipe *dispipe,
143			 struct i2c_adapter *ddc,
144			 unsigned int index)
145{
146	struct lsdc_output *output = &dispipe->output;
147	struct drm_encoder *encoder = &output->encoder;
148	struct drm_connector *connector = &output->connector;
149	int ret;
150
151	ret = drm_encoder_init(ddev, encoder, &ls7a1000_encoder_funcs[index],
152			       DRM_MODE_ENCODER_TMDS, "encoder-%u", index);
153	if (ret)
154		return ret;
155
156	encoder->possible_crtcs = BIT(index);
157
158	ret = drm_connector_init_with_ddc(ddev, connector,
159					  &ls7a1000_dpi_connector_funcs,
160					  DRM_MODE_CONNECTOR_DPI, ddc);
161	if (ret)
162		return ret;
163
164	drm_info(ddev, "display pipe-%u has a DVO\n", index);
165
166	drm_connector_helper_add(connector, &ls7a1000_dpi_connector_helpers);
167
168	drm_connector_attach_encoder(connector, encoder);
169
170	connector->polled = DRM_CONNECTOR_POLL_CONNECT |
171			    DRM_CONNECTOR_POLL_DISCONNECT;
172
173	connector->interlace_allowed = 0;
174	connector->doublescan_allowed = 0;
175
176	return 0;
177}