diff options
Diffstat (limited to 'drivers/gpu/drm/loongson/lsdc_output_7a1000.c')
-rw-r--r-- | drivers/gpu/drm/loongson/lsdc_output_7a1000.c | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/drivers/gpu/drm/loongson/lsdc_output_7a1000.c b/drivers/gpu/drm/loongson/lsdc_output_7a1000.c new file mode 100644 index 000000000000..6fc8dd1c7d9a --- /dev/null +++ b/drivers/gpu/drm/loongson/lsdc_output_7a1000.c @@ -0,0 +1,178 @@ +// 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_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) +{ + unsigned int num = 0; + struct edid *edid; + + if (conn->ddc) { + edid = drm_get_edid(conn, conn->ddc); + if (edid) { + drm_connector_update_edid_property(conn, edid); + num = drm_add_edid_modes(conn, edid); + kfree(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; +} |