// SPDX-License-Identifier: GPL-2.0 /* * Copyright (C) 2023 Loongson Technology Corporation Limited */ #include #include #include #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; }