• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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