162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (C) 2023 Loongson Technology Corporation Limited
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <drm/drm_atomic_helper.h>
762306a36Sopenharmony_ci#include <drm/drm_edid.h>
862306a36Sopenharmony_ci#include <drm/drm_probe_helper.h>
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include "lsdc_drv.h"
1162306a36Sopenharmony_ci#include "lsdc_output.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci/*
1462306a36Sopenharmony_ci * The display controller in the LS7A1000 exports two DVO interfaces, thus
1562306a36Sopenharmony_ci * external encoder is required, except connected to the DPI panel directly.
1662306a36Sopenharmony_ci *
1762306a36Sopenharmony_ci *       ___________________                                     _________
1862306a36Sopenharmony_ci *      |            -------|                                   |         |
1962306a36Sopenharmony_ci *      |  CRTC0 --> | DVO0 ----> Encoder0 ---> Connector0 ---> | Display |
2062306a36Sopenharmony_ci *      |  _   _     -------|        ^             ^            |_________|
2162306a36Sopenharmony_ci *      | | | | |  +------+ |        |             |
2262306a36Sopenharmony_ci *      | |_| |_|  | i2c6 | <--------+-------------+
2362306a36Sopenharmony_ci *      |          +------+ |
2462306a36Sopenharmony_ci *      |                   |
2562306a36Sopenharmony_ci *      |  DC in LS7A1000   |
2662306a36Sopenharmony_ci *      |                   |
2762306a36Sopenharmony_ci *      |  _   _   +------+ |
2862306a36Sopenharmony_ci *      | | | | |  | i2c7 | <--------+-------------+
2962306a36Sopenharmony_ci *      | |_| |_|  +------+ |        |             |             _________
3062306a36Sopenharmony_ci *      |            -------|        |             |            |         |
3162306a36Sopenharmony_ci *      |  CRTC1 --> | DVO1 ----> Encoder1 ---> Connector1 ---> |  Panel  |
3262306a36Sopenharmony_ci *      |            -------|                                   |_________|
3362306a36Sopenharmony_ci *      |___________________|
3462306a36Sopenharmony_ci *
3562306a36Sopenharmony_ci * Currently, we assume the external encoders connected to the DVO are
3662306a36Sopenharmony_ci * transparent. Loongson's DVO interface can directly drive RGB888 panels.
3762306a36Sopenharmony_ci *
3862306a36Sopenharmony_ci *  TODO: Add support for non-transparent encoders
3962306a36Sopenharmony_ci */
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic int ls7a1000_dpi_connector_get_modes(struct drm_connector *conn)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	unsigned int num = 0;
4462306a36Sopenharmony_ci	struct edid *edid;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	if (conn->ddc) {
4762306a36Sopenharmony_ci		edid = drm_get_edid(conn, conn->ddc);
4862306a36Sopenharmony_ci		if (edid) {
4962306a36Sopenharmony_ci			drm_connector_update_edid_property(conn, edid);
5062306a36Sopenharmony_ci			num = drm_add_edid_modes(conn, edid);
5162306a36Sopenharmony_ci			kfree(edid);
5262306a36Sopenharmony_ci		}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci		return num;
5562306a36Sopenharmony_ci	}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	num = drm_add_modes_noedid(conn, 1920, 1200);
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci	drm_set_preferred_mode(conn, 1024, 768);
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return num;
6262306a36Sopenharmony_ci}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic struct drm_encoder *
6562306a36Sopenharmony_cils7a1000_dpi_connector_get_best_encoder(struct drm_connector *connector,
6662306a36Sopenharmony_ci					struct drm_atomic_state *state)
6762306a36Sopenharmony_ci{
6862306a36Sopenharmony_ci	struct lsdc_output *output = connector_to_lsdc_output(connector);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci	return &output->encoder;
7162306a36Sopenharmony_ci}
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_cistatic const struct drm_connector_helper_funcs
7462306a36Sopenharmony_cils7a1000_dpi_connector_helpers = {
7562306a36Sopenharmony_ci	.atomic_best_encoder = ls7a1000_dpi_connector_get_best_encoder,
7662306a36Sopenharmony_ci	.get_modes = ls7a1000_dpi_connector_get_modes,
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic enum drm_connector_status
8062306a36Sopenharmony_cils7a1000_dpi_connector_detect(struct drm_connector *connector, bool force)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	struct i2c_adapter *ddc = connector->ddc;
8362306a36Sopenharmony_ci
8462306a36Sopenharmony_ci	if (ddc) {
8562306a36Sopenharmony_ci		if (drm_probe_ddc(ddc))
8662306a36Sopenharmony_ci			return connector_status_connected;
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci		return connector_status_disconnected;
8962306a36Sopenharmony_ci	}
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	return connector_status_unknown;
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_cistatic const struct drm_connector_funcs ls7a1000_dpi_connector_funcs = {
9562306a36Sopenharmony_ci	.detect = ls7a1000_dpi_connector_detect,
9662306a36Sopenharmony_ci	.fill_modes = drm_helper_probe_single_connector_modes,
9762306a36Sopenharmony_ci	.destroy = drm_connector_cleanup,
9862306a36Sopenharmony_ci	.reset = drm_atomic_helper_connector_reset,
9962306a36Sopenharmony_ci	.atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
10062306a36Sopenharmony_ci	.atomic_destroy_state = drm_atomic_helper_connector_destroy_state
10162306a36Sopenharmony_ci};
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_cistatic void ls7a1000_pipe0_encoder_reset(struct drm_encoder *encoder)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	struct drm_device *ddev = encoder->dev;
10662306a36Sopenharmony_ci	struct lsdc_device *ldev = to_lsdc(ddev);
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	/*
10962306a36Sopenharmony_ci	 * We need this for S3 support, screen will not lightup if don't set
11062306a36Sopenharmony_ci	 * this register correctly.
11162306a36Sopenharmony_ci	 */
11262306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC0_DVO_CONF_REG,
11362306a36Sopenharmony_ci		    PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic void ls7a1000_pipe1_encoder_reset(struct drm_encoder *encoder)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	struct drm_device *ddev = encoder->dev;
11962306a36Sopenharmony_ci	struct lsdc_device *ldev = to_lsdc(ddev);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	/*
12262306a36Sopenharmony_ci	 * We need this for S3 support, screen will not lightup if don't set
12362306a36Sopenharmony_ci	 * this register correctly.
12462306a36Sopenharmony_ci	 */
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/* DVO */
12762306a36Sopenharmony_ci	lsdc_wreg32(ldev, LSDC_CRTC1_DVO_CONF_REG,
12862306a36Sopenharmony_ci		    BIT(31) | PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN);
12962306a36Sopenharmony_ci}
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_cistatic const struct drm_encoder_funcs ls7a1000_encoder_funcs[2] = {
13262306a36Sopenharmony_ci	{
13362306a36Sopenharmony_ci		.reset = ls7a1000_pipe0_encoder_reset,
13462306a36Sopenharmony_ci		.destroy = drm_encoder_cleanup,
13562306a36Sopenharmony_ci	},
13662306a36Sopenharmony_ci	{
13762306a36Sopenharmony_ci		.reset = ls7a1000_pipe1_encoder_reset,
13862306a36Sopenharmony_ci		.destroy = drm_encoder_cleanup,
13962306a36Sopenharmony_ci	},
14062306a36Sopenharmony_ci};
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ciint ls7a1000_output_init(struct drm_device *ddev,
14362306a36Sopenharmony_ci			 struct lsdc_display_pipe *dispipe,
14462306a36Sopenharmony_ci			 struct i2c_adapter *ddc,
14562306a36Sopenharmony_ci			 unsigned int index)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct lsdc_output *output = &dispipe->output;
14862306a36Sopenharmony_ci	struct drm_encoder *encoder = &output->encoder;
14962306a36Sopenharmony_ci	struct drm_connector *connector = &output->connector;
15062306a36Sopenharmony_ci	int ret;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	ret = drm_encoder_init(ddev, encoder, &ls7a1000_encoder_funcs[index],
15362306a36Sopenharmony_ci			       DRM_MODE_ENCODER_TMDS, "encoder-%u", index);
15462306a36Sopenharmony_ci	if (ret)
15562306a36Sopenharmony_ci		return ret;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	encoder->possible_crtcs = BIT(index);
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	ret = drm_connector_init_with_ddc(ddev, connector,
16062306a36Sopenharmony_ci					  &ls7a1000_dpi_connector_funcs,
16162306a36Sopenharmony_ci					  DRM_MODE_CONNECTOR_DPI, ddc);
16262306a36Sopenharmony_ci	if (ret)
16362306a36Sopenharmony_ci		return ret;
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	drm_info(ddev, "display pipe-%u has a DVO\n", index);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	drm_connector_helper_add(connector, &ls7a1000_dpi_connector_helpers);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	drm_connector_attach_encoder(connector, encoder);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	connector->polled = DRM_CONNECTOR_POLL_CONNECT |
17262306a36Sopenharmony_ci			    DRM_CONNECTOR_POLL_DISCONNECT;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	connector->interlace_allowed = 0;
17562306a36Sopenharmony_ci	connector->doublescan_allowed = 0;
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	return 0;
17862306a36Sopenharmony_ci}
179