1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177
|
// 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)
{
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;
}
|