1 /*
2 * Copyright (C) 2019 Allwinnertech Co.Ltd
3 * Authors: zhengwanyu
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 */
11 #include <drm/drm_crtc_helper.h>
12 #include <drm/drm_fb_helper.h>
13 #include <drm/drm_plane_helper.h>
14 #include <drm/drm_atomic_helper.h>
15 #include <drm/drm_print.h>
16
17 #include "sunxi_drm_drv.h"
18 #include "sunxi_drm_crtc.h"
19 #include "sunxi_drm_encoder.h"
20 #include "sunxi_device/sunxi_tcon.h"
21 #include "sunxi_device/sunxi_de.h"
22 #ifdef CONFIG_AW_DRM_LCD
23 #include "sunxi_device/sunxi_lcd.h"
24 #endif
25 #ifdef CONFIG_AW_DRM_TV
26 #include "sunxi_device/sunxi_tv.h"
27 #endif
28
29 static unsigned int sunxi_encoder_cnt;
30 static struct sunxi_drm_encoder *sunxi_encoder;
31
sunxi_drm_encoder_get_count(void)32 unsigned int sunxi_drm_encoder_get_count(void)
33 {
34 return sunxi_encoder_cnt;
35 }
36
sunxi_drm_encoder_in_use(struct sunxi_drm_encoder * sencoder)37 static bool sunxi_drm_encoder_in_use(struct sunxi_drm_encoder *sencoder)
38 {
39 struct drm_connector *connector;
40 struct drm_encoder *encoder = &sencoder->encoder;
41 struct drm_device *dev = encoder->dev;
42
43 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
44 if (connector->encoder == encoder)
45 return true;
46 }
47
48 return false;
49 }
50
sunxi_drm_encoder_get_attached_connector_id(struct drm_encoder * encoder)51 int sunxi_drm_encoder_get_attached_connector_id(struct drm_encoder *encoder)
52 {
53 struct drm_connector *connector;
54 struct drm_device *dev = encoder->dev;
55
56 list_for_each_entry(connector, &dev->mode_config.connector_list, head) {
57 if (connector->encoder == encoder)
58 return connector->index;
59 }
60
61 return -1;
62 }
63
64
sunxi_drm_encoder_show(char * buf,struct drm_device * dev)65 ssize_t sunxi_drm_encoder_show(char *buf, struct drm_device *dev)
66 {
67 ssize_t n = 0;
68 int conn_id;
69 struct drm_encoder *encoder;
70
71 drm_for_each_encoder(encoder, dev) {
72 n += sprintf(buf + n, "encoder id:%d obj_id:%d\n", encoder->index,
73 encoder->base.id);
74 n += sprintf(buf + n, "name:%s\n", encoder->name);
75 n += sprintf(buf + n, "type:%d\n", encoder->encoder_type);
76 n += sprintf(buf + n, "possible_crtcs:%u\n", encoder->possible_crtcs);
77 n += sprintf(buf + n, "possible_clones:%u\n", encoder->possible_clones);
78 if (encoder->crtc)
79 n += sprintf(buf + n, "attached crtc id:%d\n", encoder->crtc->index);
80 else
81 n += sprintf(buf + n, "NO attached crtc\n");
82
83 conn_id = sunxi_drm_encoder_get_attached_connector_id(encoder);
84 if (conn_id >= 0)
85 n += sprintf(buf + n, "attached connector id:%d\n", conn_id);
86 else
87 n += sprintf(buf + n, "NO attached connector\n");
88
89 n += sprintf(buf + n, "\n");
90 }
91
92 return n;
93 }
94
sunxi_encoder_reset(struct drm_encoder * encoder)95 static void sunxi_encoder_reset(struct drm_encoder *encoder)
96 {
97 DRM_INFO("*************%s************\n", __func__);
98 return;
99 }
100
sunxi_encoder_disable(struct drm_encoder * encoder)101 static void sunxi_encoder_disable(struct drm_encoder *encoder)
102 {
103 int i;
104 unsigned int irq_no;
105
106 struct drm_crtc *crtc;
107 struct sunxi_drm_crtc *scrtc;
108
109 struct sunxi_drm_connector *sconn;
110
111 struct sunxi_drm_encoder *sunxi_enc =
112 to_sunxi_encoder(encoder);
113
114 DRM_INFO("[SUNXI-ENCODER]%s\n", __func__);
115 if (!encoder->crtc) {
116 DRM_ERROR("This ENCODER has NO attached crtc\n");
117 return;
118 }
119 crtc = encoder->crtc;
120 scrtc = to_sunxi_crtc(crtc);
121
122 for (i = 0; i < sunxi_drm_connector_get_count(); i++) {
123 sconn = sunxi_drm_connector_get_connector(i);
124 if (sconn->connector.encoder != encoder)
125 continue;
126
127 if (sconn->use_irq)
128 irq_no = sconn->irq_no;
129 else if (sunxi_enc->use_irq)
130 irq_no = sunxi_enc->irq_no;
131 else
132 DRM_INFO("WARN: NO irq for tcon%d and lcd%d\n",
133 sunxi_enc->encoder_id, sconn->con_id);
134
135 scrtc->irq_unregister(scrtc, irq_no);
136 sconn->disable(sconn);
137 sunxi_enc->hw_funcs->unset(sunxi_enc->encoder_id);
138 sunxi_tcon_unattach_connector_type(sunxi_enc->encoder_id);
139 }
140
141 return;
142 }
143
sunxi_encoder_enable(struct drm_encoder * encoder)144 static void sunxi_encoder_enable(struct drm_encoder *encoder)
145 {
146 int i;
147 unsigned int irq_no;
148 struct disp_video_timings p_info;
149 struct sunxi_connector_work_mode work_mode;
150
151 struct drm_crtc *crtc;
152 struct sunxi_drm_crtc *scrtc;
153 struct drm_crtc_state *crtc_state;
154
155 /*struct drm_connector *conn;*/
156 struct sunxi_drm_connector *sconn;
157 struct drm_connector_state *conn_state;
158
159 struct sunxi_drm_encoder *sunxi_enc =
160 to_sunxi_encoder(encoder);
161
162 #ifdef CONFIG_VIDEO_SUNXI_CAR_REVERSE
163 if (sunxi_drm_get_force_plane_en())
164 return;
165 #endif
166
167 DRM_DEBUG_DRIVER("[SUNXI-ENCODER]%s\n", __func__);
168 if (!encoder->crtc) {
169 DRM_ERROR("This ENCODER has NO attached crtc\n");
170 return;
171 }
172 crtc = encoder->crtc;
173 scrtc = to_sunxi_crtc(crtc);
174 crtc_state = crtc->state;
175
176 for (i = 0; i < sunxi_drm_connector_get_count(); i++) {
177 if (!((crtc_state->connector_mask >> i) & 0x1))
178 continue;
179
180 sconn = sunxi_drm_connector_get_connector(i);
181 conn_state = sconn->connector.state;
182 if (conn_state->best_encoder != encoder) {
183 DRM_ERROR("connector%d state's best_encoder is NOT encoder%d",
184 i, sunxi_enc->encoder_id);
185 continue;
186 }
187
188 /* set an encoder working for a certain kind of connector,
189 * NOTE: if you want to use the callback functions(hw_funcs) of a encoder,
190 * you must call sunxi_tcon_attach_connector_type() to set a tcon to a kind of
191 * connector.
192 */
193 sunxi_enc->hw_funcs =
194 sunxi_tcon_attach_connector_type(sunxi_enc->encoder_id,
195 sconn->type);
196 sunxi_enc->use_irq =
197 sunxi_enc->hw_funcs->is_use_irq(sunxi_enc->encoder_id);
198 sunxi_enc->irq_no =
199 sunxi_enc->hw_funcs->get_irq_no(sunxi_enc->encoder_id);
200
201 if (sconn->use_irq)
202 irq_no = sconn->irq_no;
203 else if (sunxi_enc->use_irq)
204 irq_no = sunxi_enc->irq_no;
205 else
206 DRM_INFO("WARN: NO irq for tcon%d and lcd%d\n",
207 sunxi_enc->encoder_id, sconn->con_id);
208
209 if (sconn->get_video_timing)
210 sconn->get_video_timing(&p_info, &crtc_state->mode);
211
212 if (scrtc->irq_register(scrtc, irq_no) < 0) {
213 DRM_ERROR("sunxi_drm_crtc_irq_register failed\n");
214 return;
215 }
216
217 if (sconn->get_work_mode)
218 sconn->get_work_mode(sconn, &work_mode);
219
220 sunxi_enc->hw_funcs->set(scrtc->crtc_id,
221 sunxi_enc->encoder_id,
222 sconn->type_id,
223 &p_info, &work_mode);
224 sconn->enable(sconn, &crtc_state->mode);
225 }
226
227 return;
228 }
229
sunxi_encoder_connector_is_supported(struct sunxi_drm_encoder * senc,struct sunxi_drm_connector * sconn)230 static bool sunxi_encoder_connector_is_supported(
231 struct sunxi_drm_encoder *senc,
232 struct sunxi_drm_connector *sconn)
233
234 {
235 return senc->hw_funcs->conn_is_support(senc->encoder_id,
236 sconn->con_id);
237 }
238
239
sunxi_encoder_sw_enable(struct sunxi_drm_encoder * sunxi_enc)240 static void sunxi_encoder_sw_enable(struct sunxi_drm_encoder *sunxi_enc)
241 {
242 int irq_no;
243 struct drm_crtc *crtc;
244 struct sunxi_drm_crtc *scrtc;
245 struct drm_crtc_state *crtc_state;
246
247 /*struct drm_connector *conn;*/
248 struct sunxi_drm_connector *sconn;
249 struct drm_encoder *encoder = &sunxi_enc->encoder;
250
251 DRM_INFO("[SUNXI-ENCODER]%s\n", __func__);
252 if (!encoder->crtc) {
253 DRM_ERROR("This ENCODER has NO attached crtc\n");
254 return;
255 }
256 crtc = encoder->crtc;
257 crtc_state = crtc->state;
258 scrtc = to_sunxi_crtc(crtc);
259
260 sconn = sunxi_drm_connector_get_connector(0);
261
262 /* set an encoder working for a certain kind of connector,
263 * NOTE: if you want to use the callback functions(hw_funcs) of a encoder,
264 * you must call sunxi_tcon_attach_connector_type() to set a tcon to a kind of
265 * connector.
266 */
267 sunxi_enc->hw_funcs =
268 sunxi_tcon_attach_connector_type(sunxi_enc->encoder_id, sconn->type);
269 sunxi_enc->use_irq =
270 sunxi_enc->hw_funcs->is_use_irq(sunxi_enc->encoder_id);
271 sunxi_enc->irq_no =
272 sunxi_enc->hw_funcs->get_irq_no(sunxi_enc->encoder_id);
273
274 if (sconn->use_irq)
275 irq_no = sconn->irq_no;
276 else if (sunxi_enc->use_irq)
277 irq_no = sunxi_enc->irq_no;
278 else
279 DRM_INFO("WARN: NO irq for tcon%d and lcd%d\n",
280 sunxi_enc->encoder_id, sconn->con_id);
281
282 if (scrtc->irq_register(scrtc, irq_no) < 0) {
283 DRM_ERROR("sunxi_drm_crtc_irq_register failed\n");
284 return;
285 }
286
287 sunxi_enc->hw_funcs->sw_set(sunxi_enc->encoder_id, sconn->type_id);
288 sconn->sw_enable(sconn, 0);
289 return;
290 }
291
292
293 static const struct drm_encoder_funcs sunxi_encoder_funcs = {
294 .reset = sunxi_encoder_reset,
295 .destroy = drm_encoder_cleanup,
296 };
297
298 static const struct drm_encoder_helper_funcs sunxi_encoder_helper_funcs = {
299 //.atomic_check = sunxi_encoder_atomic_check,
300 .disable = sunxi_encoder_disable,
301 .enable = sunxi_encoder_enable,
302 //.mode_set = sunxi_encoder_mode_set,
303 };
304
305 /*init all of the encoders*/
sunxi_drm_encoder_init(struct drm_device * dev)306 int sunxi_drm_encoder_init(struct drm_device *dev)
307 {
308 int i, ret, max_en;
309 struct sunxi_drm_encoder *enc;
310 struct drm_crtc *crtc;
311 struct sunxi_drm_crtc *scrtc;
312
313 max_en = sunxi_tcon_get_count();
314 sunxi_encoder_cnt = max_en;
315 sunxi_encoder = kzalloc(
316 max_en * sizeof(struct sunxi_drm_encoder), GFP_KERNEL);
317 if (!sunxi_encoder) {
318 DRM_ERROR("can NOT allocate memory for sunxi_encoder\n");
319 goto en_err;
320 }
321 sunxi_encoder_cnt = max_en;
322
323 for (i = 0; i < max_en; i++) {
324 enc = &sunxi_encoder[i];
325 enc->encoder_id = i;
326
327 list_for_each_entry(crtc,
328 &dev->mode_config.crtc_list, head) {
329 scrtc = to_sunxi_crtc(crtc);
330 if (scrtc->encoder_is_supported(scrtc, enc))
331 enc->encoder.possible_crtcs |= 1 << scrtc->crtc_id;
332 }
333
334 ret = drm_encoder_init(dev, &enc->encoder,
335 &sunxi_encoder_funcs,
336 DRM_MODE_ENCODER_NONE,
337 "sunxi-encoder%d", i);
338 if (ret < 0) {
339 DRM_ERROR("drm_encoder_init failed\n");
340 return -1;
341 }
342
343 drm_encoder_helper_add(&enc->encoder,
344 &sunxi_encoder_helper_funcs);
345
346 /* NOTE: since tcon has NOT been attached a sunxi_tcon_funcs, now we can NOT
347 * use enc->hw_funcs
348 */
349 //enc->use_irq = enc->hw_funcs->is_use_irq(encoder->index);
350 //enc->irq_no = enc->hw_funcs->get_irq_no(encoder->index);
351
352 enc->is_in_use = sunxi_drm_encoder_in_use;
353 enc->conn_is_supported = sunxi_encoder_connector_is_supported;
354 enc->sw_enable = sunxi_encoder_sw_enable;
355 }
356
357 return 0;
358 en_err:
359 for (i = 0; i < max_en; i++) {
360 enc = &sunxi_encoder[i];
361 drm_encoder_cleanup(&enc->encoder);
362 }
363 kfree(sunxi_encoder);
364 DRM_ERROR("sunxi encoder init failed\n");
365 return -1;
366 }
367
sunxi_drm_encoder_exit(struct drm_device * dev)368 void sunxi_drm_encoder_exit(struct drm_device *dev)
369 {
370 int i;
371 struct sunxi_drm_encoder *enc;
372
373 for (i = 0; i < sunxi_encoder_cnt; i++) {
374 if (!sunxi_encoder)
375 break;
376 enc = &sunxi_encoder[i];
377 if (!enc)
378 return;
379 drm_encoder_cleanup(&enc->encoder);
380 }
381
382 kfree(sunxi_encoder);
383 sunxi_encoder = NULL;
384 }
385