178 lines · c
1// SPDX-License-Identifier: GPL-2.02/*3 * Copyright (C) 2023 Loongson Technology Corporation Limited4 */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, thus15 * 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 are36 * transparent. Loongson's DVO interface can directly drive RGB888 panels.37 *38 * TODO: Add support for non-transparent encoders39 */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_funcs73ls7a1000_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_status79ls7a1000_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_state100};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 set109 * 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 set122 * 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}178