| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- // SPDX-License-Identifier: GPL-2.0
- /*
- * Copyright (C) 2023 Loongson Technology Corporation Limited
- */
- #include <drm/drm_atomic_helper.h>
- #include <drm/drm_edid.h>
- #include <drm/drm_print.h>
- #include <drm/drm_probe_helper.h>
- #include "lsdc_drv.h"
- #include "lsdc_output.h"
- /*
- * The display controller in the LS7A1000 exports two DVO interfaces, thus
- * external encoder is required, except connected to the DPI panel directly.
- *
- * ___________________ _________
- * | -------| | |
- * | CRTC0 --> | DVO0 ----> Encoder0 ---> Connector0 ---> | Display |
- * | _ _ -------| ^ ^ |_________|
- * | | | | | +------+ | | |
- * | |_| |_| | i2c6 | <--------+-------------+
- * | +------+ |
- * | |
- * | DC in LS7A1000 |
- * | |
- * | _ _ +------+ |
- * | | | | | | i2c7 | <--------+-------------+
- * | |_| |_| +------+ | | | _________
- * | -------| | | | |
- * | CRTC1 --> | DVO1 ----> Encoder1 ---> Connector1 ---> | Panel |
- * | -------| |_________|
- * |___________________|
- *
- * Currently, we assume the external encoders connected to the DVO are
- * transparent. Loongson's DVO interface can directly drive RGB888 panels.
- *
- * TODO: Add support for non-transparent encoders
- */
- static int ls7a1000_dpi_connector_get_modes(struct drm_connector *conn)
- {
- int num;
- if (conn->ddc) {
- const struct drm_edid *drm_edid;
- drm_edid = drm_edid_read(conn);
- drm_edid_connector_update(conn, drm_edid);
- num = drm_edid_connector_add_modes(conn);
- drm_edid_free(drm_edid);
- return num;
- }
- num = drm_add_modes_noedid(conn, 1920, 1200);
- drm_set_preferred_mode(conn, 1024, 768);
- return num;
- }
- static struct drm_encoder *
- ls7a1000_dpi_connector_get_best_encoder(struct drm_connector *connector,
- struct drm_atomic_state *state)
- {
- struct lsdc_output *output = connector_to_lsdc_output(connector);
- return &output->encoder;
- }
- static const struct drm_connector_helper_funcs
- ls7a1000_dpi_connector_helpers = {
- .atomic_best_encoder = ls7a1000_dpi_connector_get_best_encoder,
- .get_modes = ls7a1000_dpi_connector_get_modes,
- };
- static enum drm_connector_status
- ls7a1000_dpi_connector_detect(struct drm_connector *connector, bool force)
- {
- struct i2c_adapter *ddc = connector->ddc;
- if (ddc) {
- if (drm_probe_ddc(ddc))
- return connector_status_connected;
- return connector_status_disconnected;
- }
- return connector_status_unknown;
- }
- static const struct drm_connector_funcs ls7a1000_dpi_connector_funcs = {
- .detect = ls7a1000_dpi_connector_detect,
- .fill_modes = drm_helper_probe_single_connector_modes,
- .destroy = drm_connector_cleanup,
- .reset = drm_atomic_helper_connector_reset,
- .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
- .atomic_destroy_state = drm_atomic_helper_connector_destroy_state
- };
- static void ls7a1000_pipe0_encoder_reset(struct drm_encoder *encoder)
- {
- struct drm_device *ddev = encoder->dev;
- struct lsdc_device *ldev = to_lsdc(ddev);
- /*
- * We need this for S3 support, screen will not lightup if don't set
- * this register correctly.
- */
- lsdc_wreg32(ldev, LSDC_CRTC0_DVO_CONF_REG,
- PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN);
- }
- static void ls7a1000_pipe1_encoder_reset(struct drm_encoder *encoder)
- {
- struct drm_device *ddev = encoder->dev;
- struct lsdc_device *ldev = to_lsdc(ddev);
- /*
- * We need this for S3 support, screen will not lightup if don't set
- * this register correctly.
- */
- /* DVO */
- lsdc_wreg32(ldev, LSDC_CRTC1_DVO_CONF_REG,
- BIT(31) | PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN);
- }
- static const struct drm_encoder_funcs ls7a1000_encoder_funcs[2] = {
- {
- .reset = ls7a1000_pipe0_encoder_reset,
- .destroy = drm_encoder_cleanup,
- },
- {
- .reset = ls7a1000_pipe1_encoder_reset,
- .destroy = drm_encoder_cleanup,
- },
- };
- int ls7a1000_output_init(struct drm_device *ddev,
- struct lsdc_display_pipe *dispipe,
- struct i2c_adapter *ddc,
- unsigned int index)
- {
- struct lsdc_output *output = &dispipe->output;
- struct drm_encoder *encoder = &output->encoder;
- struct drm_connector *connector = &output->connector;
- int ret;
- ret = drm_encoder_init(ddev, encoder, &ls7a1000_encoder_funcs[index],
- DRM_MODE_ENCODER_TMDS, "encoder-%u", index);
- if (ret)
- return ret;
- encoder->possible_crtcs = BIT(index);
- ret = drm_connector_init_with_ddc(ddev, connector,
- &ls7a1000_dpi_connector_funcs,
- DRM_MODE_CONNECTOR_DPI, ddc);
- if (ret)
- return ret;
- drm_info(ddev, "display pipe-%u has a DVO\n", index);
- drm_connector_helper_add(connector, &ls7a1000_dpi_connector_helpers);
- drm_connector_attach_encoder(connector, encoder);
- connector->polled = DRM_CONNECTOR_POLL_CONNECT |
- DRM_CONNECTOR_POLL_DISCONNECT;
- connector->interlace_allowed = 0;
- connector->doublescan_allowed = 0;
- return 0;
- }
|