]> Git Repo - linux.git/blob - drivers/gpu/drm/loongson/lsdc_output_7a1000.c
Merge tag 'platform-drivers-x86-v6.7-6' of git://git.kernel.org/pub/scm/linux/kernel...
[linux.git] / drivers / gpu / drm / loongson / lsdc_output_7a1000.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2023 Loongson Technology Corporation Limited
4  */
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, thus
15  * 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 are
36  * transparent. Loongson's DVO interface can directly drive RGB888 panels.
37  *
38  *  TODO: Add support for non-transparent encoders
39  */
40
41 static int ls7a1000_dpi_connector_get_modes(struct drm_connector *conn)
42 {
43         unsigned int num = 0;
44         struct edid *edid;
45
46         if (conn->ddc) {
47                 edid = drm_get_edid(conn, conn->ddc);
48                 if (edid) {
49                         drm_connector_update_edid_property(conn, edid);
50                         num = drm_add_edid_modes(conn, edid);
51                         kfree(edid);
52                 }
53
54                 return num;
55         }
56
57         num = drm_add_modes_noedid(conn, 1920, 1200);
58
59         drm_set_preferred_mode(conn, 1024, 768);
60
61         return num;
62 }
63
64 static struct drm_encoder *
65 ls7a1000_dpi_connector_get_best_encoder(struct drm_connector *connector,
66                                         struct drm_atomic_state *state)
67 {
68         struct lsdc_output *output = connector_to_lsdc_output(connector);
69
70         return &output->encoder;
71 }
72
73 static const struct drm_connector_helper_funcs
74 ls7a1000_dpi_connector_helpers = {
75         .atomic_best_encoder = ls7a1000_dpi_connector_get_best_encoder,
76         .get_modes = ls7a1000_dpi_connector_get_modes,
77 };
78
79 static enum drm_connector_status
80 ls7a1000_dpi_connector_detect(struct drm_connector *connector, bool force)
81 {
82         struct i2c_adapter *ddc = connector->ddc;
83
84         if (ddc) {
85                 if (drm_probe_ddc(ddc))
86                         return connector_status_connected;
87
88                 return connector_status_disconnected;
89         }
90
91         return connector_status_unknown;
92 }
93
94 static const struct drm_connector_funcs ls7a1000_dpi_connector_funcs = {
95         .detect = ls7a1000_dpi_connector_detect,
96         .fill_modes = drm_helper_probe_single_connector_modes,
97         .destroy = drm_connector_cleanup,
98         .reset = drm_atomic_helper_connector_reset,
99         .atomic_duplicate_state = drm_atomic_helper_connector_duplicate_state,
100         .atomic_destroy_state = drm_atomic_helper_connector_destroy_state
101 };
102
103 static void ls7a1000_pipe0_encoder_reset(struct drm_encoder *encoder)
104 {
105         struct drm_device *ddev = encoder->dev;
106         struct lsdc_device *ldev = to_lsdc(ddev);
107
108         /*
109          * We need this for S3 support, screen will not lightup if don't set
110          * this register correctly.
111          */
112         lsdc_wreg32(ldev, LSDC_CRTC0_DVO_CONF_REG,
113                     PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN);
114 }
115
116 static void ls7a1000_pipe1_encoder_reset(struct drm_encoder *encoder)
117 {
118         struct drm_device *ddev = encoder->dev;
119         struct lsdc_device *ldev = to_lsdc(ddev);
120
121         /*
122          * We need this for S3 support, screen will not lightup if don't set
123          * this register correctly.
124          */
125
126         /* DVO */
127         lsdc_wreg32(ldev, LSDC_CRTC1_DVO_CONF_REG,
128                     BIT(31) | PHY_CLOCK_POL | PHY_CLOCK_EN | PHY_DATA_EN);
129 }
130
131 static const struct drm_encoder_funcs ls7a1000_encoder_funcs[2] = {
132         {
133                 .reset = ls7a1000_pipe0_encoder_reset,
134                 .destroy = drm_encoder_cleanup,
135         },
136         {
137                 .reset = ls7a1000_pipe1_encoder_reset,
138                 .destroy = drm_encoder_cleanup,
139         },
140 };
141
142 int ls7a1000_output_init(struct drm_device *ddev,
143                          struct lsdc_display_pipe *dispipe,
144                          struct i2c_adapter *ddc,
145                          unsigned int index)
146 {
147         struct lsdc_output *output = &dispipe->output;
148         struct drm_encoder *encoder = &output->encoder;
149         struct drm_connector *connector = &output->connector;
150         int ret;
151
152         ret = drm_encoder_init(ddev, encoder, &ls7a1000_encoder_funcs[index],
153                                DRM_MODE_ENCODER_TMDS, "encoder-%u", index);
154         if (ret)
155                 return ret;
156
157         encoder->possible_crtcs = BIT(index);
158
159         ret = drm_connector_init_with_ddc(ddev, connector,
160                                           &ls7a1000_dpi_connector_funcs,
161                                           DRM_MODE_CONNECTOR_DPI, ddc);
162         if (ret)
163                 return ret;
164
165         drm_info(ddev, "display pipe-%u has a DVO\n", index);
166
167         drm_connector_helper_add(connector, &ls7a1000_dpi_connector_helpers);
168
169         drm_connector_attach_encoder(connector, encoder);
170
171         connector->polled = DRM_CONNECTOR_POLL_CONNECT |
172                             DRM_CONNECTOR_POLL_DISCONNECT;
173
174         connector->interlace_allowed = 0;
175         connector->doublescan_allowed = 0;
176
177         return 0;
178 }
This page took 0.03747 seconds and 4 git commands to generate.