• 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_fb_helper.h>
12 #include <drm/drm_plane_helper.h>
13 #include <drm/drm_crtc_helper.h>
14 #include <drm/drm_atomic_helper.h>
15 #include <drm/drm_sysfs.h>
16 #include <drm/drm_probe_helper.h>
17 #include <drm/drm_print.h>
18 #include <video/sunxi_display2.h>
19 
20 #include "drm_internal.h"
21 #include "sunxi_drm_drv.h"
22 #include "sunxi_drm_encoder.h"
23 #include "sunxi_drm_connector.h"
24 
25 #include "sunxi_device/sunxi_lcd.h"
26 
27 #if defined(CONFIG_AW_DRM_BACKLIGHT)
28 #include "sunxi_device/sunxi_backlight.h"
29 #endif
30 
31 #include "de/include.h"
32 
33 /*
34  * transform lcd_type defined in sunxi_lcd to drm connector type
35  */
sunxi_connector_lcd_type_trans(int sunxi_lcd_type)36 static int sunxi_connector_lcd_type_trans(int sunxi_lcd_type)
37 {
38 	switch (sunxi_lcd_type) {
39 	case LCD_IF_HV:
40 		return DRM_MODE_CONNECTOR_Unknown;
41 	case LCD_IF_CPU:
42 		return DRM_MODE_CONNECTOR_Unknown;
43 	case LCD_IF_LVDS:
44 		return DRM_MODE_CONNECTOR_LVDS;
45 	case LCD_IF_DSI:
46 		return DRM_MODE_CONNECTOR_DSI;
47 	case LCD_IF_EDP:
48 		return DRM_MODE_CONNECTOR_eDP;
49 	}
50 
51 	DRM_ERROR("wrong sunxi_lcd_type:%d\n", sunxi_lcd_type);
52 	return -1;
53 }
54 
55 
sunxi_connector_lcd_convert_panel_to_display_mode(struct drm_display_mode * mode,struct disp_panel_para * panel)56 static void sunxi_connector_lcd_convert_panel_to_display_mode(
57 				struct drm_display_mode *mode,
58 				struct disp_panel_para  *panel)
59 {
60 	mode->clock       = panel->lcd_dclk_freq * 1000;
61 
62 	mode->hdisplay    = panel->lcd_x;
63 	mode->hsync_start = panel->lcd_ht - panel->lcd_hbp;
64 	mode->hsync_end   = panel->lcd_ht - panel->lcd_hbp + panel->lcd_hspw;
65 	mode->htotal      = panel->lcd_ht;
66 
67 	mode->vdisplay    = panel->lcd_y;
68 	mode->vsync_start = panel->lcd_vt - panel->lcd_vbp;
69 	mode->vsync_end   = panel->lcd_vt - panel->lcd_vbp + panel->lcd_vspw;
70 	mode->vtotal      = panel->lcd_vt;
71 	mode->vscan       = 0;
72 	mode->flags       = 0;
73 	mode->width_mm    = panel->lcd_width;
74 	mode->height_mm   = panel->lcd_height;
75 	/* mode->vrefresh    = mode->clock * 1000 / mode->vtotal / mode->htotal;
76 	DRM_DEBUG_KMS("Modeline %d:%d %d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
77 		mode->base.id, mode->vrefresh, mode->clock,
78 		mode->hdisplay, mode->hsync_start,
79 		mode->hsync_end, mode->htotal,
80 		mode->vdisplay, mode->vsync_start,
81 		mode->vsync_end, mode->vtotal, mode->type, mode->flags); */
82 	DRM_DEBUG_KMS("Modeline:%d %d %d %d %d %d %d %d %d 0x%x 0x%x\n",
83 		mode->clock,
84 		mode->hdisplay, mode->hsync_start,
85 		mode->hsync_end, mode->htotal,
86 		mode->vdisplay, mode->vsync_start,
87 		mode->vsync_end, mode->vtotal, mode->type, mode->flags);
88 	DRM_DEBUG_KMS("panel: clk[%d] [x %d, ht %d, hbp %d, hspw %d]\n",
89 		panel->lcd_dclk_freq * 1000,
90 		panel->lcd_x, panel->lcd_ht,
91 		panel->lcd_hbp, panel->lcd_hspw);
92 	DRM_DEBUG_KMS("[y%d, vt%d, bp %d, pw %d] %dx%d\n",
93 		panel->lcd_y, panel->lcd_vt,
94 		panel->lcd_vbp, panel->lcd_vspw, panel->lcd_width,
95 		panel->lcd_height);
96 }
97 
sunxi_connector_lcd_detect(struct drm_connector * connector,bool force)98 static enum drm_connector_status sunxi_connector_lcd_detect(
99 				struct drm_connector *connector,
100 				bool force)
101 {
102 	return connector_status_connected;
103 }
104 
sunxi_connector_lcd_get_modes(struct drm_connector * connector)105 static int sunxi_connector_lcd_get_modes(struct drm_connector *connector)
106 {
107 	struct drm_display_mode *mode;
108 	struct sunxi_drm_connector *sunxi_con = to_sunxi_connector(connector);
109 	struct disp_panel_para *panel_para = NULL;
110 	struct sunxi_lcd_funcs *lcd_funcs
111 		= (struct sunxi_lcd_funcs *)sunxi_con->hw_funcs;
112 
113 
114 	mode = drm_mode_create(connector->dev);
115 	if (!mode) {
116 		DRM_ERROR("failed to create a new display mode.\n");
117 		return 0;
118 	}
119 
120 	panel_para = lcd_funcs->get_panel_para(sunxi_con->type_id);
121 	if (!panel_para) {
122 		DRM_ERROR("get lcd%d panel para failed\n",
123 			sunxi_con->type_id);
124 		return -1;
125 	}
126 
127 	sunxi_connector_lcd_convert_panel_to_display_mode(mode, panel_para);
128 	connector->display_info.width_mm = mode->width_mm;
129 	connector->display_info.height_mm = mode->height_mm;
130 
131 	mode->type = DRM_MODE_TYPE_DRIVER | DRM_MODE_TYPE_PREFERRED;
132 	drm_mode_set_name(mode);
133 	drm_mode_probed_add(connector, mode);
134 
135 	return 1;
136 }
137 
sunxi_connector_lcd_mode_valid(struct drm_connector * connector,struct drm_display_mode * mode)138 static int sunxi_connector_lcd_mode_valid(struct drm_connector *connector,
139 					struct drm_display_mode *mode)
140 {
141 	return MODE_OK;
142 }
143 
144 
145 /**
146  * @name       :sunxi_connector_register
147  * @brief      :register sysfs backlight
148  * @param[IN]  :connector:pointer of drm_connector
149  * @return     :0 if success, -1 else
150  */
sunxi_connector_lcd_register(struct drm_connector * connector)151 int sunxi_connector_lcd_register(struct drm_connector *connector)
152 {
153 	int ret = -1;
154 #if defined(CONFIG_AW_DRM_BACKLIGHT)
155 	struct sunxi_drm_connector *sconn = to_sunxi_connector(connector);
156 
157 	if (!sconn) {
158 		DRM_ERROR("Null sunxi connector pointer!\n");
159 		goto OUT;
160 	}
161 
162 	if (sconn->type != DISP_OUTPUT_TYPE_LCD) {
163 		DRM_INFO("[WARN]connector:%d is NOT lcd!\n", connector->index);
164 		ret = 0;
165 		goto OUT;
166 	}
167 
168 	ret = sunxi_backlight_device_register(connector->kdev, sconn->type_id);
169 	if (ret < 0)
170 		DRM_ERROR("sunxi_backlight_device_register for lcd:%d failed\n",
171 			sconn->type_id);
172 #else
173 	DRM_INFO("[WARN]: NOT support backlight for connector:%d\n", connector->index);
174 	ret = 0;
175 #endif
176 
177 OUT:
178 	return ret;
179 }
180 
181 
sunxi_connector_lcd_unregister(struct drm_connector * connector)182 void sunxi_connector_lcd_unregister(struct drm_connector *connector)
183 {
184 #if defined(CONFIG_AW_DRM_BACKLIGHT)
185 	struct sunxi_drm_connector *sconn = to_sunxi_connector(connector);
186 
187 	if (!sconn) {
188 		DRM_ERROR("Null sunxi_drm_connector pointer!\n");
189 		return;
190 	}
191 	if (sconn->type != DISP_OUTPUT_TYPE_LCD)
192 		return;
193 
194 	sunxi_backlight_device_unregister(sconn->type_id);
195 #endif
196 
197 }
198 
199 static const struct drm_connector_funcs sunxi_connector_lcd_funcs = {
200 	.detect			= sunxi_connector_lcd_detect,
201 	.fill_modes		= drm_helper_probe_single_connector_modes,
202 	.destroy		= drm_connector_cleanup,
203 	.late_register          = sunxi_connector_lcd_register,
204 	.early_unregister	= sunxi_connector_lcd_unregister,
205 	.reset			= drm_atomic_helper_connector_reset,
206 	.atomic_duplicate_state	= drm_atomic_helper_connector_duplicate_state,
207 	.atomic_destroy_state	= drm_atomic_helper_connector_destroy_state,
208 };
209 
210 static const struct drm_connector_helper_funcs sunxi_connector_helper_lcd_funcs = {
211 	.get_modes	= sunxi_connector_lcd_get_modes,
212 	.mode_valid	= sunxi_connector_lcd_mode_valid,
213 	.best_encoder	= sunxi_connector_best_encoder,
214 };
215 
sunxi_connector_lcd_get_prefered_resolution(struct sunxi_drm_connector * sunxi_conn,unsigned int * w,unsigned int * h,unsigned int * vfresh)216 static void sunxi_connector_lcd_get_prefered_resolution(
217 		struct sunxi_drm_connector *sunxi_conn, /*unsigned int *tvmode,*/
218 		unsigned int *w, unsigned int *h, unsigned int *vfresh)
219 {
220 	struct disp_panel_para *panel_para = NULL;
221 	struct sunxi_lcd_funcs *lcd_funcs
222 			= (struct sunxi_lcd_funcs *)sunxi_conn->hw_funcs;
223 
224 	panel_para = lcd_funcs->get_panel_para(sunxi_conn->type_id);
225 	if (!panel_para) {
226 		DRM_ERROR("get lcd%d panel para failed\n",
227 			sunxi_conn->type_id);
228 		return;
229 	}
230 
231 	*w = panel_para->lcd_x;
232 	*h = panel_para->lcd_y;
233 	*vfresh = panel_para->lcd_dclk_freq * 1000 * 1000
234 		/ (panel_para->lcd_vt * panel_para->lcd_ht);
235 	DRM_INFO("w:%u h:%u vfresh:%u\n", *w, *h, *vfresh);
236 }
237 
sunxi_connector_lcd_is_use_irq(struct sunxi_drm_connector * sconn)238 bool sunxi_connector_lcd_is_use_irq(struct sunxi_drm_connector *sconn)
239 {
240 	struct sunxi_lcd_funcs *lcd_funcs
241 		= (struct sunxi_lcd_funcs *)sconn->hw_funcs;
242 
243 	return lcd_funcs->is_use_irq(sconn->type_id);
244 }
245 
sunxi_connector_lcd_enable(struct sunxi_drm_connector * sconn,struct drm_display_mode * mode)246 static int sunxi_connector_lcd_enable(struct sunxi_drm_connector *sconn,
247 					struct drm_display_mode *mode)
248 {
249 	struct sunxi_lcd_funcs *lcd_funcs
250 		= (struct sunxi_lcd_funcs *)sconn->hw_funcs;
251 
252 	return lcd_funcs->enable(sconn->type_id);
253 }
254 
255 static int
sunxi_connector_lcd_sw_enable(struct sunxi_drm_connector * sconn,struct drm_display_mode * mode)256 sunxi_connector_lcd_sw_enable(struct sunxi_drm_connector *sconn,
257 						struct drm_display_mode *mode)
258 {
259 	struct sunxi_lcd_funcs *lcd_funcs
260 		= (struct sunxi_lcd_funcs *)sconn->hw_funcs;
261 
262 	lcd_funcs->sw_enable(sconn->type_id);
263 
264 	return 0;
265 }
266 
sunxi_connector_lcd_disable(struct sunxi_drm_connector * sconn)267 static void sunxi_connector_lcd_disable(struct sunxi_drm_connector *sconn)
268 {
269 	struct sunxi_lcd_funcs *lcd_funcs
270 		= (struct sunxi_lcd_funcs *)sconn->hw_funcs;
271 
272 	lcd_funcs->disable(sconn->type_id);
273 }
274 
275 struct sunxi_drm_connector *
sunxi_drm_connector_lcd_create(struct drm_device * dev,int conn_id,int lcd_id)276 sunxi_drm_connector_lcd_create(struct drm_device *dev, int conn_id, int lcd_id)
277 {
278 	struct drm_encoder *enc;
279 	struct sunxi_drm_encoder *sunxi_enc;
280 	struct sunxi_drm_connector *sunxi_conn;
281 	int lcd_type = 0;
282 	int drm_con_type;
283 	int ret;
284 	struct sunxi_lcd_funcs *lcd_funcs;
285 
286 	sunxi_conn = kzalloc(sizeof(struct sunxi_drm_connector), GFP_KERNEL);
287 	if (!sunxi_conn) {
288 		DRM_ERROR("can NOT allocate memory for sunxi_connector\n");
289 		goto lcd_conn_err;
290 	}
291 
292 	sunxi_conn->con_id = conn_id;
293 	sunxi_conn->type_id = lcd_id;
294 	sunxi_conn->type = DISP_OUTPUT_TYPE_LCD;
295 
296 	lcd_funcs = sunxi_lcd_get_hw_funcs(lcd_id);
297 	if (!lcd_funcs) {
298 		DRM_ERROR("lcd:%d has NO funcs\n", lcd_id);
299 		goto lcd_conn_err;
300 	}
301 	sunxi_conn->hw_funcs = lcd_funcs;
302 	lcd_type = lcd_funcs->get_type(lcd_id);
303 	drm_con_type
304 		= sunxi_connector_lcd_type_trans(lcd_type);
305 	if (drm_con_type < 0) {
306 		DRM_ERROR("get drm connector type failed\n");
307 		goto lcd_conn_err;
308 	}
309 
310 	sunxi_conn->connector.connector_type = drm_con_type;
311 	sunxi_conn->connector.interlace_allowed = false;
312 	sunxi_conn->connector.polled = 0;
313 
314 	/*search for all of the encoders that can be attached to this connector*/
315 	list_for_each_entry(enc,
316 		&dev->mode_config.encoder_list, head) {
317 		sunxi_enc = to_sunxi_encoder(enc);
318 
319 		/* set an encoder working for a certain kind of connector,
320 		  * NOTE: if you want to use the callback functions(hw_funcs) of a encoder,
321 		  * you must call sunxi_tcon_attach_connector_type() to set a tcon to a kind of
322 		  * connector.
323 		  */
324 		sunxi_enc->hw_funcs =
325 			sunxi_tcon_attach_connector_type(sunxi_enc->encoder_id,
326 			DISP_OUTPUT_TYPE_LCD);
327 
328 		if (sunxi_enc->conn_is_supported(
329 			sunxi_enc, sunxi_conn)) {
330 			ret = drm_connector_attach_encoder(
331 					&sunxi_conn->connector, enc);
332 			if (ret) {
333 				DRM_ERROR("failed to attach a"
334 				"connector to a encoder\n");
335 				goto lcd_conn_err;
336 			}
337 		}
338 
339 		sunxi_tcon_unattach_connector_type(sunxi_enc->encoder_id);
340 		sunxi_enc->hw_funcs = NULL;
341 	}
342 
343 	sunxi_conn->connector.dpms = DRM_MODE_DPMS_OFF;
344 
345 	ret = drm_connector_init(dev, &sunxi_conn->connector,
346 				&sunxi_connector_lcd_funcs,
347 					drm_con_type);
348 	if (ret < 0) {
349 		DRM_ERROR("drm_connector_init failed\n");
350 		goto lcd_conn_err;
351 	}
352 
353 	drm_connector_helper_add(&sunxi_conn->connector,
354 			&sunxi_connector_helper_lcd_funcs);
355 
356 	drm_atomic_helper_connector_reset(&sunxi_conn->connector);
357 
358 	sunxi_conn->use_irq = lcd_funcs->is_use_irq(lcd_id);
359 	sunxi_conn->irq_no = lcd_funcs->get_irq_no(lcd_id);
360 	sunxi_conn->get_init_resolution
361 		= sunxi_connector_lcd_get_prefered_resolution;
362 	sunxi_conn->enable = sunxi_connector_lcd_enable;
363 	sunxi_conn->sw_enable = sunxi_connector_lcd_sw_enable;
364 	sunxi_conn->disable = sunxi_connector_lcd_disable;
365 	return sunxi_conn;
366 
367 lcd_conn_err:
368 	drm_connector_cleanup(&sunxi_conn->connector);
369 	kfree(sunxi_conn);
370 	return NULL;
371 }
372